[
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n\n*.bat text eol=crlf\n*.jar binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/01-bug-report-en.yml",
    "content": "name: \"[English] Bug Report\"\ndescription: \"Create a report to help us debug bugs\"\ntitle: \"[BUG] \"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this bug report!\n\n        NOTE: Be sure to put a clear and concise title **AFTER** `[BUG]` in the text box above.\n\n        NOTE: We do not provide any services such as proxies, DO NOT feedback any problems not caused by this application here.\n\n        <!-- template -->\n  - type: textarea\n    id: description\n    attributes:\n      label: \"Describe the bug\"\n      description: \"A clear and concise description of what the bug is.\"\n    validations:\n      required: true\n  - type: textarea\n    id: reproduce\n    attributes:\n      label: \"To Reproduce\"\n      description: \"Steps to reproduce the behavior:\"\n      value: |\n        Step 1: ...\n        Step 2: ...\n        Step 3: ...\n        ...\n    validations:\n      required: true\n  - type: textarea\n    id: device-info\n    attributes:\n      label: \"Device Info\"\n      description: |\n        Input your device information.\n\n        Example:\n        - Device: Pixel 4\n        - ROM: AOSP\n        - Android Version: 10\n      value: |\n        - Device:\n        - ROM:\n        - Android Version:\n    validations:\n      required: true\n  - type: textarea\n    id: app-info\n    attributes:\n      label: \"Application Info\"\n      description: |\n        Input application you are using information.\n\n        Example:\n        ```\n        - Version: 2.5.4-premium\n        - APK filename: cfa-2.5.4-premium-arm64-v8a-release.apk\n        - Distribution Channel: Google Play\n        ```\n      value: |\n        - Version:\n        - APK filename:\n        - Distribution Channel:\n    validations:\n      required: true\n  - type: textarea\n    id: configure\n    attributes:\n      render: yml\n      label: \"Configure File\"\n      description: |\n        Please paste or upload the configuration file here.\n\n        TIPS: If you only have a subscription link, please use your browser to download it.\n\n        **NOTE: Please remove proxies from the configuration file before uploading it.**\n        **NOTE: Please remove proxies from the configuration file before uploading it.**\n        **NOTE: Please remove proxies from the configuration file before uploading it.**\n    validations:\n      required: true\n  - type: textarea\n    id: logs\n    attributes:\n      render: raw\n      label: \"Logs\"\n      description: |\n        Please paste or upload the log file here.\n\n        TIPS: Please use the `Logcat` in application or `adb logcat`. `adb logcat` would be better.\n    validations:\n      required: true\n  - type: textarea\n    id: screenshot\n    attributes:\n      label: \"Screenshot\"\n      description: \"If applicable, add screenshots to help explain your problem.\"\n      placeholder: \"Optional\"\n  - type: textarea\n    id: additional\n    attributes:\n      label: \"Additional\"\n      description: \"Add any other context about the problem here.\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/02-feature-request-en.yml",
    "content": "name: \"[English] Feature Request\"\ndescription: \"Create a report to help us improve\"\ntitle: \"[Feature Request] \"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this feature request!\n\n        NOTE: Be sure to put a clear and concise title **AFTER** `[Feature Request]` in the text box above.\n\n        <!-- template -->\n  - type: textarea\n    id: \"description\"\n    attributes:\n      label: \"Feature Description\"\n      description: |\n        A clear and concise description of the feature.\n    validations:\n      required: true\n  - type: textarea\n    id: \"additional\"\n    attributes:\n      label: \"Additional\"\n      description: |\n        Add any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/03-bug-report-zh-cn.yml",
    "content": "name: \"[简体中文] 错误报告\"\ndescription: \"创建错误报告以帮助我们修正应用\"\ntitle: \"[BUG] \"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        感谢您在百忙之中填写此错误报告。\n\n        注意: 请务必在上方文本框的 `[BUG]` **之后**填写清晰明了的标题。\n\n        注意：这里不提供像是代理服务器之类的服务，请不要反馈非应用自身引起的问题。\n\n        <!-- template -->\n  - type: textarea\n    id: description\n    attributes:\n      label: \"描述此错误\"\n      description: \"请清晰简洁的描述你遇到的错误。\"\n    validations:\n      required: true\n  - type: textarea\n    id: reproduce\n    attributes:\n      label: \"如何复现该错误\"\n      description: \"复现步骤:\"\n      value: |\n        步骤 1: ...\n        步骤 2: ...\n        步骤 3: ...\n        ...\n    validations:\n      required: true\n  - type: textarea\n    id: device-info\n    attributes:\n      label: \"设备信息\"\n      description: |\n        输入您正在使用的设备信息。\n\n        例子:\n        - 机型: Pixel 4\n        - 系统类型: MIUI/AOSP\n        - Android 版本: 10\n      value: |\n        - 机型:\n        - 系统类型:\n        - Android 版本:\n    validations:\n      required: true\n  - type: textarea\n    id: app-info\n    attributes:\n      label: \"应用信息\"\n      description: |\n        输入您正在使用的应用信息。\n\n        例子:\n        ```\n        - 版本: 2.5.4-premium\n        - 安装包文件名: cfa-2.5.4-premium-arm64-v8a-release.apk\n        - 应用来源: Google Play\n        ```\n      value: |\n        - 版本:\n        - 安装包文件名:\n        - 应用来源:\n    validations:\n      required: true\n  - type: textarea\n    id: configure\n    attributes:\n      render: yml\n      label: \"配置文件\"\n      description: |\n        请在此粘贴和上传配置文件。\n\n        提示：如果您仅有一个订阅链接，请使用浏览器打开此链接以下载配置文件。\n\n        **注意: 请在上传配置文件前，移除其中的代理服务器信息。**\n        **注意: 请在上传配置文件前，移除其中的代理服务器信息。**\n        **注意: 请在上传配置文件前，移除其中的代理服务器信息。**\n    validations:\n      required: true\n  - type: textarea\n    id: logs\n    attributes:\n      render: raw\n      label: \"日志\"\n      description: |\n        请在此粘贴或上传日志。\n\n        提示: 请使用应用内的 `Logcat` 或 `adb logcat` 捕获日志. `adb logcat` 能更好地帮助侦测问题.\n    validations:\n      required: true\n  - type: textarea\n    id: screenshot\n    attributes:\n      label: \"屏幕截图\"\n      description: \"如果适用，请在此粘贴或上传屏幕截图。\"\n      placeholder: \"可选\"\n  - type: textarea\n    id: additional\n    attributes:\n      label: \"附加信息\"\n      description: \"其他的可能与改错误相关的信息。\"\n      placeholder: \"可选\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/04-feature-request-zh-cn.yml",
    "content": "name: \"[简体中文] 功能请求\"\ndescription: \"您希望的能够在应用中增加功能\"\ntitle: \"[Feature Request] \"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        感谢您在百忙之中填写此功能请求报告。\n\n        注意: 请务必在上方文本框的 `[Feature Request]` **之后**填写清晰明了的标题。\n\n        <!-- template -->\n  - type: textarea\n    id: \"description\"\n    attributes:\n      label: \"功能描述\"\n      description: |\n        简介明了的描述此功能。\n    validations:\n      required: true\n  - type: textarea\n    id: \"additional\"\n    attributes:\n      label: \"附加信息\"\n      description: |\n        与此功能相关的其他附加信息。\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: Android CI\non:\n  push:\n    branches:\n      - main\n    paths-ignore:\n      # - '.github/**'\n      - '.idea/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - '.gitmodules'\n      - '**.md'\n      - 'LICENSE'\n      - 'NOTICE'\n  pull_request:\n    paths-ignore:\n      # - '.github/**'\n      - '.idea/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - '.gitmodules'\n      - '**.md'\n      - 'LICENSE'\n      - 'NOTICE'\n  workflow_dispatch:\n\njobs:\n  Build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v3\n        with:\n          submodules: recursive\n\n      - name: Setup Java\n        uses: actions/setup-java@v3\n        with:\n          distribution: 'oracle'\n          java-version: 17\n\n      - name: Setup Go\n        uses: actions/setup-go@v3\n        with:\n          go-version: 'stable'\n\n      - name: Cache Go Files\n        uses: actions/cache@v3\n        with:\n          path: |\n            ~/.cache/go-build\n            ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}\n          restore-keys: |\n            ${{ runner.os }}-go-\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n        # with:\n          # arguments: --no-daemon assemble\n\n      - name: Create Sign File\n        run: |\n          echo ${{ secrets.SIGNING_KEY }} | base64 -d > keystore.jks\n          echo ${{ secrets.SIGNING_PROPERTIES }} | base64 -d > signing.properties\n\n      - name: Build with Gradle\n        run: |\n          ./gradlew --no-daemon assemble\n\n      - name: Find APKs\n        run: |\n          echo \"APK_FILE_RELEASE_ARM32=$(find app/build/outputs/apk/foss/release -name '*armeabi-v7a*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_RELEASE_ARM64=$(find app/build/outputs/apk/foss/release -name '*arm64-v8a*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_RELEASE_X86=$(find app/build/outputs/apk/foss/release -name '*x86-*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_RELEASE_X64=$(find app/build/outputs/apk/foss/release -name '*x86_64*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_RELEASE_UNIVERSAL=$(find app/build/outputs/apk/foss/release -name '*universal*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_DEBUG_ARM32=$(find app/build/outputs/apk/foss/debug -name '*armeabi-v7a*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_DEBUG_ARM64=$(find app/build/outputs/apk/foss/debug -name '*arm64-v8a*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_DEBUG_X86=$(find app/build/outputs/apk/foss/debug -name '*x86-*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_DEBUG_X64=$(find app/build/outputs/apk/foss/debug -name '*x86_64*')\" >> $GITHUB_ENV\n          echo \"APK_FILE_DEBUG_UNIVERSAL=$(find app/build/outputs/apk/foss/debug -name '*universal*')\" >> $GITHUB_ENV\n\n      - name: Show Artifacts SHA256\n        run: |\n          echo \"### Build Success\" >> $GITHUB_STEP_SUMMARY\n          echo \"|Artifact|SHA256|\" >> $GITHUB_STEP_SUMMARY\n          echo \"|:--------:|:----------|\" >> $GITHUB_STEP_SUMMARY\n          # Release Artifacts\n          release_arm32=($(sha256sum ${{ env.APK_FILE_RELEASE_ARM32 }}))\n          echo \"|release_armeabi-v7a|$release_arm32\" >> $GITHUB_STEP_SUMMARY\n          release_arm64=($(sha256sum ${{ env.APK_FILE_RELEASE_ARM64 }}))\n          echo \"|release_arm64-v8a|$release_arm64\" >> $GITHUB_STEP_SUMMARY\n          release_x86=($(sha256sum ${{ env.APK_FILE_RELEASE_X86 }}))\n          echo \"|release_x86|$release_x86\" >> $GITHUB_STEP_SUMMARY\n          release_x64=($(sha256sum ${{ env.APK_FILE_RELEASE_X64 }}))\n          echo \"|release_x86_64|$release_x64\" >> $GITHUB_STEP_SUMMARY\n          release_universal=($(sha256sum ${{ env.APK_FILE_RELEASE_UNIVERSAL }}))\n          echo \"|release_universal|$release_universal\" >> $GITHUB_STEP_SUMMARY\n          # Debug Artifacts\n          debug_arm32=($(sha256sum ${{ env.APK_FILE_DEBUG_ARM32 }}))\n          echo \"|debug_armeabi-v7a|$debug_arm32\" >> $GITHUB_STEP_SUMMARY\n          debug_arm64=($(sha256sum ${{ env.APK_FILE_DEBUG_ARM64 }}))\n          echo \"|debug_arm64-v8a|$debug_arm64\" >> $GITHUB_STEP_SUMMARY\n          debug_x86=($(sha256sum ${{ env.APK_FILE_DEBUG_X86 }}))\n          echo \"|debug_x86|$debug_x86\" >> $GITHUB_STEP_SUMMARY\n          debug_x64=($(sha256sum ${{ env.APK_FILE_DEBUG_X64 }}))\n          echo \"|debug_x86_64|$debug_x64\" >> $GITHUB_STEP_SUMMARY\n          debug_universal=($(sha256sum ${{ env.APK_FILE_DEBUG_UNIVERSAL }}))\n          echo \"|debug_universal|$debug_universal\" >> $GITHUB_STEP_SUMMARY\n\n      - name: Upload Release APK (armeabi-v7a)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_RELEASE_ARM32 }}\n          name: ClashYou-release-armeabi-v7a-${{ github.event.head_commit.id }}\n\n      - name: Upload Release APK (arm64-v8a)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_RELEASE_ARM64 }}\n          name: ClashYou-release-arm64-v8a-${{ github.event.head_commit.id }}\n\n      - name: Upload Release APK (x86)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_RELEASE_X86 }}\n          name: ClashYou-release-x86-${{ github.event.head_commit.id }}\n\n      - name: Upload Release APK (x86_64)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_RELEASE_X64 }}\n          name: ClashYou-release-x86_64-${{ github.event.head_commit.id }}\n\n      - name: Upload Release APK (Universal)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_RELEASE_UNIVERSAL }}\n          name: ClashYou-release-universal-${{ github.event.head_commit.id }}\n\n      - name: Upload Debug APK (armeabi-v7a)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_DEBUG_ARM32 }}\n          name: ClashYou-debug-armeabi-v7a-${{ github.event.head_commit.id }}\n\n      - name: Upload Debug APK (arm64-v8a)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_DEBUG_ARM64 }}\n          name: ClashYou-debug-arm64-v8a-${{ github.event.head_commit.id }}\n\n      - name: Upload Debug APK (x86)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_DEBUG_X86 }}\n          name: ClashYou-debug-x86-${{ github.event.head_commit.id }}\n\n      - name: Upload Debug APK (x86_64)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_DEBUG_X64 }}\n          name: ClashYou-debug-x86_64-${{ github.event.head_commit.id }}\n\n      - name: Upload Debug APK (Universal)\n        uses: actions/upload-artifact@v3\n        with:\n          path: ${{ env.APK_FILE_DEBUG_UNIVERSAL }}\n          name: ClashYou-debug-universal-${{ github.event.head_commit.id }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\nbuild/\n/app/foss/release\n/app/premium/release\n/captures\n\n# Ignore Gradle GUI config\ngradle-app.setting\n\n# Avoid ignoring Gradle wrapper jar targetFile (.jar files are usually ignored)\n!gradle-wrapper.jar\n\n# Cache of project\n.gradletasknamecache\n\n# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898\n# gradle/wrapper/gradle-wrapper.properties\n\n# Ignore IDEA config\n*.iml\n/.idea/*\n/core/src/main/golang/.idea/*\n/core/src/foss/golang/.idea/*\n/core/src/premium/golang/.idea/*\n\n# KeyStore\nsigning.properties\n*.keystore\n*.jks\n\n# clion cmake build\ncmake-build-*\n\n# local.properties\nlocal.properties\n\n\n# tracker\ntracker.properties\n\n# vscode\n.vscode\n\n# cxx\n.cxx\n\n*.hprof\n\n# firebase\ngoogle-services.json\n\n# Dolphin\n.directory\n\n# logs\n*.log\n\n# MacOS\n.DS_Store\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"clash-foss\"]\n\tpath = core/src/foss/golang/clash\n\turl = https://github.com/xuhaoyang/ClashForAndroid.git\n[submodule \"clash-premium\"]\n\tpath = core/src/premium/golang/clash\n\turl = https://github.com/xuhaoyang/ClashForAndroid.git\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Contributing to Clash for Android\n\n#### Code Style\n\nPlease use `Android Studio` or `Intellij IDEA` to open the project and use the project code style profile.\n\n`File` -> `Settings` -> `Editor` -> `Code Style` -> `C/C++ and Kotlin` -> `Scheme` -> `Project`\n\n\n\n#### License\n\nContributing to Clash for Android that assumes you allow code to be merged into closed-source branch of Clash for Android. Other terms follow the [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html)\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "NOTICE",
    "content": "3th-party software licenses\n\n * Clash\n==========================================================================\n                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n\n * Android Open Source Project\n * Android X Support Library\n==========================================================================\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n   1. Definitions.\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      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\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      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\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      \"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      \"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      \"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      \"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      \"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   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   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   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      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\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      (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      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   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   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   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   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   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   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "PRIVACY_POLICY.md",
    "content": "## Privacy Policy\n\nThe Clash for Android is built as an Open Source software. This app is provided by personal at no cost and is intended for use as is.\n\nThis page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our app.\n\n**Information Collection and Use**\n\nWe will not upload any of your personally information and that will be stored in the internal storage or memory.\n\nWe collect the following information and store it in memory, and such information will be destroyed when the application is fully exited.\n\n- Installed Applications\n\n  This data is used for the PROCESS-NAME rule.\n\n**Log Data**\n\nWe do not collect log data unless you use log collector.\n\n**Cookies**\n\nCookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.\n\nThis app does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this app.\n\n**Security**\n\nWe value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.\n\n**Links to Other Sites**\n\nThis app may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.\n\n**Changes to This Privacy Policy**\n\nWe may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.\n\n**Contact Us**\n\nIf you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.\n"
  },
  {
    "path": "README.md",
    "content": "## Clash You\n\n📕 [English Version](./README_en.md)\n\n基于 [Clash for Android]()，为安卓设备设计的 [Clash]() GUI，使用 Material You 设计语言。\n\n可在 [Releases](https://github.com/Yos-X/ClashYou/releases) 获取最新发布版本，也可在 [Actions](https://github.com/Yos-X/ClashYou/actions) 获取 CI 版（需要登录，感谢 [@淡い夏](https://github.com/lightsummer233)）\n\n### 版本特性\n\n- 适配新安卓版本权限\n- 应用主题支持动态取色\n- 遵循 MD3 设计风格的 UI\n\n### 注意\n\nClash You 基于的 Clash for Android **已是最终版本**，进入**长久不更新**状态。\n\n因此 Clash You 使用的旧内核将可能**不支持**新 Clash 内核的部分特性。\n\n若想使用 Clash 的**较新特性**，可以考虑~原作者~ ？？？？？ 正在开发的 ？？？？？ 项目。\n\nTelegram Channel： ？？？？？\n\n### 特性\n\n完整 [Clash]() 特性实现\n\n### 运行环境要求\n\n- Android 5.0+ (最低)\n- Android 12.0+ (推荐)\n- `armeabi-v7a` , `arm64-v8a`, `x86` 或 `x86_64` 架构\n\n### 许可证\n\n参见 [LICENSE](./LICENSE) 与 [NOTICE](./NOTICE)\n\n### 隐私协议\n\n参见 [隐私协议](./PRIVACY_POLICY.md)\n\n### 构建\n\n1. 更新子模块（IDEA 项目内 `终端`）\n   ```sh\n   git submodule update --init --recursive\n   ```\n2. 安装 **OpenJDK 11**, **Android SDK**, **CMake** 和 **Golang**\n3. 在项目根目录新建 `local.properties`，并写入以下内容\n   ```properties\n   sdk.dir=/path/to/android-sdk\n   ```\n4. 在项目根目录新建 `signing.properties`，并写入以下内容\n   ```properties\n   keystore.path=/path/to/keystore/file（签名密钥路径）\n   keystore.password=<签名密钥密码>\n   key.alias=<签名密钥别名>\n   key.password=<签名密钥密码>\n   ```\n5. 构建\n   ```sh\n   ./gradlew app:assembleFossRelease\n   ```\n6. 输出文件 `app-<version>-foss-<arch>-release.apk` 在 `app/build/outputs/apk/foss/release/` 目录下\n"
  },
  {
    "path": "README_en.md",
    "content": "## Clash You\n\n**⚠ This page is translated by GPT 4.**\n\nBased on [Clash for Android](),\na [Clash]() GUI designed for Android devices, using the Material\nYou design language.\n\nThe latest Release version can be obtained from\n[Releases](https://github.com/Yos-X/ClashYou/releases)\nand CI version can be obtained from\n[Actions](https://github.com/Yos-X/ClashYou/actions) (login is required, thanks to [@Light_summer](https://github.com/lightsummer233)).\n\n### Version Features\n\n- Adapted to new Android version permissions\n- Application theme supports dynamic color picking\n- UI following MD3 design style\n\n### Attention\n\nClash You is based on **the final version** of Clash for Android, which has entered **a long-term non update state**.\n\nTherefore, the old core used by Clash You may **not support** some features of the new Clash core.\n\nFor **the newer features** of Clash, consider the ????? project being developed by ~the original author~ ?????.\n\nTelegram Channel：?????\n\n### Feature\n\nFully feature of [Clash]()\n\n### Runtime Requirements\n\n- Android 5.0+ (minimum)\n- Android 12.0+ (recommended)\n- `armeabi-v7a`, `arm64-v8a`, `x86` or `x86_64` architecture\n\n### License\n\nSee [LICENSE](./LICENSE) and [NOTICE](./NOTICE)\n\n### Privacy Policy\n\nSee [Privacy Policy](./PRIVACY_POLICY.md)\n\n### Building\n\n1. Update submodules (in IDEA project `terminal`)\n   ```sh\n   git submodule update --init --recursive\n   ```\n2. Install **OpenJDK 11**, **Android SDK**, **CMake** and **Golang**\n3. Create a new `local.properties` file in the project root directory and write the following content\n   ```properties\n   sdk.dir=/path/to/android-sdk\n   ```\n4. Create a new `signing.properties` file in the project root directory and write the following content\n   ```properties\n   keystore.path=/path/to/keystore/file\n   keystore.password=<keystore password>\n   key.alias=<key alias>\n   key.password=<key password>\n   ```\n5. Build\n   ```sh\n   ./gradlew app:assembleFossRelease\n   ```\n6. Output file `app-<version>-foss-<arch>-release.apk` is located in the `app/build/outputs/apk/foss/release/` directory.\n"
  },
  {
    "path": "app/build.gradle.kts",
    "content": "plugins {\n    kotlin(\"android\")\n    kotlin(\"kapt\")\n    id(\"com.android.application\")\n}\n\ndependencies {\n    repositories {\n        mavenLocal()\n        mavenCentral()\n        gradlePluginPortal()\n        google()\n        maven(\"https://jitpack.io\")\n        maven(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n        maven(\"https://maven.kr328.app/releases\")\n    }\n    compileOnly(project(\":hideapi\"))\n\n    implementation(project(\":core\"))\n    implementation(project(\":service\"))\n    implementation(project(\":design\"))\n    implementation(project(\":common\"))\n\n    implementation(libs.kotlin.coroutine)\n    implementation(libs.androidx.core)\n    implementation(libs.androidx.activity)\n    implementation(libs.androidx.fragment)\n    implementation(libs.androidx.appcompat)\n    implementation(libs.androidx.coordinator)\n    implementation(libs.androidx.recyclerview)\n    implementation(libs.google.material)\n    implementation(libs.androidx.splashscreen)\n    implementation(libs.getactivity.xxpermission)\n}\n\ntasks.getByName(\"clean\", type = Delete::class) {\n    delete(file(\"release\"))\n}\n/*\nandroid {\n    defaultConfig {\n        applicationId = \"yos.clash.material\"\n    }\n}\n*/\n"
  },
  {
    "path": "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.\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-dontobfuscate\n\n-assumenosideeffects class kotlin.jvm.internal.Intrinsics {\n    public static void checkNotNull(...);\n    public static void checkExpressionValueIsNotNull(...);\n    public static void checkNotNullExpressionValue(...);\n    public static void checkReturnedValueIsNotNull(...);\n    public static void checkFieldIsNotNull(...);\n    public static void checkParameterIsNotNull(...);\n    public static void checkNotNullParameter(...);\n}\n\n# Kotlin Coroutine\n# Allow R8 to optimize away the FastServiceLoader.\n# Together with ServiceLoader optimization in R8\n# this results in direct instantiation when loading Dispatchers.Main\n-assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader {\n    boolean FAST_SERVICE_LOADER_ENABLED return false;\n}\n\n-assumenosideeffects class kotlinx.coroutines.internal.FastServiceLoaderKt {\n    boolean ANDROID_DETECTED return true;\n}\n\n-keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;}\n\n# Disable support for \"Missing Main Dispatcher\", since we always have Android main dispatcher\n-assumenosideeffects class kotlinx.coroutines.internal.MainDispatchersKt {\n    boolean SUPPORT_MISSING return false;\n}\n\n# Statically turn off all debugging facilities and assertions\n-assumenosideeffects class kotlinx.coroutines.DebugKt {\n    boolean getASSERTIONS_ENABLED() return false;\n    boolean getDEBUG() return false;\n    boolean getRECOVER_STACK_TRACES() return false;\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"yos.clash.material\">\n\n    <uses-feature\n        android:name=\"android.software.leanback\"\n        android:required=\"false\" />\n    <uses-feature\n        android:name=\"android.hardware.touchscreen\"\n        android:required=\"false\" />\n\n    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission\n        android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"\n        tools:ignore=\"ScopedStorage\" />\n    <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n    <!--suppress AndroidDomInspection -->\n    <application\n        android:name=\".MainApplication\"\n        android:allowBackup=\"true\"\n        android:banner=\"@mipmap/ic_banner\"\n        android:fullBackupContent=\"@xml/full_backup_content\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/application_name\"\n        android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:requestLegacyExternalStorage=\"true\"\n        android:enableOnBackInvokedCallback=\"true\"\n        android:theme=\"@style/BootstrapTheme\"\n        tools:ignore=\"DataExtractionRules,GoogleAppIndexingWarning\"\n        tools:targetApi=\"tiramisu\">\n        <meta-data\n            android:name=\"releaseName\"\n            android:value=\"@string/release_name\" />\n        <meta-data\n            android:name=\"releaseCode\"\n            android:value=\"@integer/release_code\" />\n\n        <!--suppress AndroidDomInspection -->\n        <activity\n            android:name=\".MainActivity\"\n            android:theme=\"@style/SplashTheme\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"true\"\n            android:label=\"@string/launch_name\"\n            android:launchMode=\"singleTop\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE_PREFERENCES\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ExternalImportActivity\"\n            android:exported=\"true\"\n            android:label=\"@string/import_from_file\"\n            android:theme=\"@android:style/Theme.Translucent.NoTitleBar.Fullscreen\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data\n                    android:host=\"install-config\"\n                    android:scheme=\"clash\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ApkBrokenActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/application_broken\" />\n        <activity\n            android:name=\".AppCrashedActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/application_crashed\"\n            android:launchMode=\"singleTask\" />\n        <activity\n            android:name=\".ProfilesActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/profiles\" />\n        <activity\n            android:name=\".NewProfileActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/create_profile\" />\n        <activity\n            android:name=\".PropertiesActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/profile\" />\n        <activity\n            android:name=\".ProxyActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/proxy\" />\n        <activity\n            android:name=\".ProvidersActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/providers\" />\n        <activity\n            android:name=\".LogsActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/logs\" />\n        <activity\n            android:name=\".LogcatActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/logcat\" />\n        <activity\n            android:name=\".SettingsActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/settings\" />\n        <activity\n            android:name=\".NetworkSettingsActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/network\" />\n        <activity\n            android:name=\".AppSettingsActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/app\" />\n        <activity\n            android:name=\".OverrideSettingsActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/override\" />\n        <activity\n            android:name=\".AccessControlActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/access_control_packages\" />\n        <activity\n            android:name=\".HelpActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/help\" />\n        <activity\n            android:name=\".FilesActivity\"\n            android:configChanges=\"uiMode\"\n            android:exported=\"false\"\n            android:label=\"@string/files\" />\n\n        <service\n            android:name=\".LogcatService\"\n            android:exported=\"false\"\n            android:foregroundServiceType=\"specialUse\"\n            android:label=\"@string/clash_logcat\" />\n        <service\n            android:name=\".TileService\"\n            android:foregroundServiceType=\"specialUse\"\n            android:exported=\"true\"\n            android:icon=\"@drawable/ic_logo_service\"\n            android:label=\"@string/launch_name\"\n            android:permission=\"android.permission.BIND_QUICK_SETTINGS_TILE\">\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE\" />\n            </intent-filter>\n        </service>\n\n        <receiver\n            android:name=\".RestartReceiver\"\n            android:foregroundServiceType=\"specialUse\"\n            android:enabled=\"false\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.BOOT_COMPLETED\" />\n                <action android:name=\"android.intent.action.MY_PACKAGE_REPLACED\" />\n            </intent-filter>\n        </receiver>\n    </application>\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/AccessControlActivity.kt",
    "content": "package yos.clash.material\n\nimport android.Manifest.permission.INTERNET\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport android.content.pm.ApplicationInfo\nimport android.content.pm.PackageInfo\nimport android.content.pm.PackageManager\nimport androidx.core.content.getSystemService\nimport yos.clash.material.design.AccessControlDesign\nimport yos.clash.material.design.model.AppInfo\nimport yos.clash.material.design.util.toAppInfo\nimport yos.clash.material.service.store.ServiceStore\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\n\nclass AccessControlActivity : BaseActivity<AccessControlDesign>() {\n    override suspend fun main() {\n        val service = ServiceStore(this)\n\n        val selected = withContext(Dispatchers.IO) {\n            service.accessControlPackages.toMutableSet()\n        }\n\n        defer {\n            withContext(Dispatchers.IO) {\n                service.accessControlPackages = selected\n            }\n        }\n\n        val design = AccessControlDesign(this, uiStore, selected)\n\n        setContentDesign(design)\n\n        design.requests.send(AccessControlDesign.Request.ReloadApps)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        AccessControlDesign.Request.ReloadApps -> {\n                            design.patchApps(loadApps(selected))\n                        }\n                        AccessControlDesign.Request.SelectAll -> {\n                            val all = withContext(Dispatchers.Default) {\n                                design.apps.map(AppInfo::packageName)\n                            }\n\n                            selected.clear()\n                            selected.addAll(all)\n\n                            design.rebindAll()\n                        }\n                        AccessControlDesign.Request.SelectNone -> {\n                            selected.clear()\n\n                            design.rebindAll()\n                        }\n                        AccessControlDesign.Request.SelectInvert -> {\n                            val all = withContext(Dispatchers.Default) {\n                                design.apps.map(AppInfo::packageName).toSet() - selected\n                            }\n\n                            selected.clear()\n                            selected.addAll(all)\n\n                            design.rebindAll()\n                        }\n                        AccessControlDesign.Request.Import -> {\n                            val clipboard = getSystemService<ClipboardManager>()\n                            val data = clipboard?.primaryClip\n\n                            if (data != null && data.itemCount > 0) {\n                                val packages = data.getItemAt(0).text.split(\"\\n\").toSet()\n                                val all = design.apps.map(AppInfo::packageName).intersect(packages)\n\n                                selected.clear()\n                                selected.addAll(all)\n                            }\n\n                            design.rebindAll()\n                        }\n                        AccessControlDesign.Request.Export -> {\n                            val clipboard = getSystemService<ClipboardManager>()\n\n                            val data = ClipData.newPlainText(\n                                \"packages\",\n                                selected.joinToString(\"\\n\")\n                            )\n\n                            clipboard?.setPrimaryClip(data)\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private suspend fun loadApps(selected: Set<String>): List<AppInfo> =\n        withContext(Dispatchers.IO) {\n            val reverse = uiStore.accessControlReverse\n            val sort = uiStore.accessControlSort\n            val systemApp = uiStore.accessControlSystemApp\n\n            val base = compareByDescending<AppInfo> { it.packageName in selected }\n            val comparator = if (reverse) base.thenDescending(sort) else base.then(sort)\n\n            val pm = packageManager\n            val packages = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS)\n\n            packages.asSequence()\n                .filter {\n                    it.packageName != packageName\n                }\n                .filter {\n                    it.packageName == \"android\" || it.requestedPermissions?.contains(INTERNET) == true\n                }\n                .filter {\n                    systemApp || !it.isSystemApp\n                }\n                .map {\n                    it.toAppInfo(pm)\n                }\n                .sortedWith(comparator)\n                .toList()\n        }\n\n    private val PackageInfo.isSystemApp: Boolean\n        get() {\n            return applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0\n        }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/ApkBrokenActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.Intent\nimport android.net.Uri\nimport yos.clash.material.design.ApkBrokenDesign\nimport kotlinx.coroutines.isActive\n\nclass ApkBrokenActivity : BaseActivity<ApkBrokenDesign>() {\n    override suspend fun main() {\n        val design = ApkBrokenDesign(this)\n\n        setContentDesign(design)\n\n        while (isActive) {\n            val req = design.requests.receive()\n\n            startActivity(Intent(Intent.ACTION_VIEW).setData(Uri.parse(req.url)))\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/AppCrashedActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.common.compat.versionCodeCompat\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.design.AppCrashedDesign\nimport yos.clash.material.log.SystemLogcat\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.withContext\n\nclass AppCrashedActivity : BaseActivity<AppCrashedDesign>() {\n    override suspend fun main() {\n        val design = AppCrashedDesign(this)\n\n        setContentDesign(design)\n\n        val packageInfo = withContext(Dispatchers.IO) {\n            packageManager.getPackageInfo(packageName, 0)\n        }\n\n        Log.i(\"App version: versionName = ${packageInfo.versionName} versionCode = ${packageInfo.versionCodeCompat}\")\n\n        val logs = withContext(Dispatchers.IO) {\n            SystemLogcat.dumpCrash()\n        }\n\n        design.setAppLogs(logs)\n\n        while (isActive) {\n            events.receive()\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/AppSettingsActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.pm.PackageManager\nimport yos.clash.material.common.util.componentName\nimport yos.clash.material.design.AppSettingsDesign\nimport yos.clash.material.design.model.Behavior\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.util.ApplicationObserver\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\n\nclass AppSettingsActivity : BaseActivity<AppSettingsDesign>(), Behavior {\n    override suspend fun main() {\n        val design = AppSettingsDesign(\n            this,\n            uiStore,\n            ServiceStore(this),\n            this,\n            clashRunning,\n        )\n\n        setContentDesign(design)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ClashStart, Event.ClashStop, Event.ServiceRecreated ->\n                            recreate()\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    ApplicationObserver.createdActivities.forEach {\n                        it.recreate()\n                    }\n                }\n            }\n        }\n    }\n\n    override var autoRestart: Boolean\n        get() {\n            val status = packageManager.getComponentEnabledSetting(\n                RestartReceiver::class.componentName\n            )\n\n            return status == PackageManager.COMPONENT_ENABLED_STATE_ENABLED\n        }\n        set(value) {\n            val status = if (value)\n                PackageManager.COMPONENT_ENABLED_STATE_ENABLED\n            else\n                PackageManager.COMPONENT_ENABLED_STATE_DISABLED\n\n            packageManager.setComponentEnabledSetting(\n                RestartReceiver::class.componentName,\n                status,\n                PackageManager.DONT_KILL_APP,\n            )\n        }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/BaseActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.res.Configuration\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.View\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen\nimport com.google.android.material.color.DynamicColors\nimport yos.clash.material.common.compat.isAllowForceDarkCompat\nimport yos.clash.material.common.compat.isLightNavigationBarCompat\nimport yos.clash.material.common.compat.isLightStatusBarsCompat\nimport yos.clash.material.common.compat.isSystemBarsTranslucentCompat\nimport com.github.kr328.clash.core.bridge.ClashException\nimport yos.clash.material.design.Design\nimport yos.clash.material.design.model.DarkMode\nimport yos.clash.material.design.store.UiStore\nimport yos.clash.material.design.ui.DayNight\nimport yos.clash.material.design.util.resolveThemedBoolean\nimport yos.clash.material.design.util.resolveThemedColor\nimport yos.clash.material.design.util.showExceptionToast\nimport yos.clash.material.remote.Broadcasts\nimport yos.clash.material.remote.Remote\nimport yos.clash.material.util.ActivityResultLifecycle\nimport yos.clash.material.util.ApplicationObserver\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.channels.Channel\nimport java.util.concurrent.atomic.AtomicInteger\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.suspendCoroutine\n\nabstract class BaseActivity<D : Design<*>> :\n    AppCompatActivity(),\n    CoroutineScope by MainScope(),\n    Broadcasts.Observer {\n    enum class Event {\n        ServiceRecreated,\n        ActivityStart,\n        ActivityStop,\n        ClashStop,\n        ClashStart,\n        ProfileLoaded,\n        ProfileChanged\n    }\n\n\n    protected val uiStore by lazy { UiStore(this) }\n    protected val events = Channel<Event>(Channel.UNLIMITED)\n    protected var activityStarted: Boolean = false\n    protected val clashRunning: Boolean\n        get() = Remote.broadcasts.clashRunning\n    protected var design: D? = null\n        private set(value) {\n            field = value\n\n            if (value != null) {\n                setContentView(value.root)\n            } else {\n                setContentView(View(this))\n            }\n        }\n\n    private var defer: suspend () -> Unit = {}\n    private var deferRunning = false\n    private val nextRequestKey = AtomicInteger(0)\n    private var dayNight: DayNight = DayNight.Day\n\n    protected abstract suspend fun main()\n\n    fun defer(operation: suspend () -> Unit) {\n        this.defer = operation\n    }\n\n    suspend fun <I, O> startActivityForResult(\n        contracts: ActivityResultContract<I, O>,\n        input: I\n    ): O = withContext(Dispatchers.Main) {\n        val requestKey = nextRequestKey.getAndIncrement().toString()\n\n        ActivityResultLifecycle().use { lifecycle, start ->\n            suspendCoroutine { c ->\n                activityResultRegistry.register(requestKey, lifecycle, contracts) {\n                    c.resumeWith(Result.success(it))\n                }.apply { start() }.launch(input)\n            }\n        }\n    }\n\n    suspend fun setContentDesign(design: D) {\n        suspendCoroutine<Unit> {\n            window.decorView.post {\n                this.design = design\n\n                it.resume(Unit)\n            }\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        DynamicColors.applyToActivitiesIfAvailable(this.application)\n\n        applyDayNight()\n\n        launch {\n            main()\n\n            finish()\n        }\n    }\n\n    override fun onStart() {\n        super.onStart()\n\n        activityStarted = true\n\n        Remote.broadcasts.addObserver(this)\n\n        events.trySend(Event.ActivityStart)\n    }\n\n    override fun onStop() {\n        super.onStop()\n\n        activityStarted = false\n\n        Remote.broadcasts.removeObserver(this)\n\n        events.trySend(Event.ActivityStop)\n    }\n\n    override fun onDestroy() {\n        design?.cancel()\n\n        cancel()\n\n        super.onDestroy()\n    }\n\n    override fun finish() {\n        if (deferRunning) {\n            return\n        }\n\n        deferRunning = true\n\n        launch {\n            try {\n                defer()\n            } finally {\n                withContext(NonCancellable) {\n                    super.finish()\n                }\n            }\n        }\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n\n        if (queryDayNight(newConfig) != dayNight) {\n            ApplicationObserver.createdActivities.forEach {\n                it.recreate()\n            }\n        }\n    }\n\n    open fun shouldDisplayHomeAsUpEnabled(): Boolean {\n        return true\n    }\n\n    override fun onSupportNavigateUp(): Boolean {\n        this.onBackPressed()\n\n        return true\n    }\n\n    override fun onProfileChanged() {\n        events.trySend(Event.ProfileChanged)\n    }\n\n    override fun onProfileLoaded() {\n        events.trySend(Event.ProfileLoaded)\n    }\n\n    override fun onServiceRecreated() {\n        events.trySend(Event.ServiceRecreated)\n    }\n\n    override fun onStarted() {\n        events.trySend(Event.ClashStart)\n    }\n\n    override fun onStopped(cause: String?) {\n        events.trySend(Event.ClashStop)\n\n        if (cause != null && activityStarted) {\n            launch {\n                design?.showExceptionToast(ClashException(cause))\n            }\n        }\n    }\n\n    private fun queryDayNight(config: Configuration = resources.configuration): DayNight {\n        return when (uiStore.darkMode) {\n            DarkMode.Auto -> {\n                if (config.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)\n                    DayNight.Night\n                else\n                    DayNight.Day\n            }\n            DarkMode.ForceLight -> {\n                DayNight.Day\n            }\n            DarkMode.ForceDark -> {\n                DayNight.Night\n            }\n        }\n    }\n\n    private fun applyDayNight(config: Configuration = resources.configuration) {\n        val dayNight = queryDayNight(config)\n\n        when (dayNight) {\n            DayNight.Night -> {\n                theme.applyStyle(R.style.AppThemeDark, true)\n            }\n            DayNight.Day -> {\n                theme.applyStyle(R.style.AppThemeLight, true)\n            }\n        }\n\n        window.isAllowForceDarkCompat = false\n        window.isSystemBarsTranslucentCompat = true\n\n        window.statusBarColor = resolveThemedColor(android.R.attr.statusBarColor)\n        window.navigationBarColor = resolveThemedColor(android.R.attr.navigationBarColor)\n\n        if (Build.VERSION.SDK_INT >= 23) {\n            window.isLightStatusBarsCompat =\n                resolveThemedBoolean(android.R.attr.windowLightStatusBar)\n        }\n\n        if (Build.VERSION.SDK_INT >= 27) {\n            window.isLightNavigationBarCompat =\n                resolveThemedBoolean(android.R.attr.windowLightNavigationBar)\n        }\n\n        this.dayNight = dayNight\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/ExternalImportActivity.kt",
    "content": "package yos.clash.material\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport yos.clash.material.R\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.setUUID\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.util.withProfile\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.launch\nimport java.util.*\n\nclass ExternalImportActivity : Activity(), CoroutineScope by MainScope() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        if (intent.action != Intent.ACTION_VIEW)\n            return finish()\n\n        val uri = intent.data ?: return finish()\n        val url = uri.getQueryParameter(\"url\") ?: return finish()\n\n        launch {\n            val uuid = withProfile {\n                val type = when (uri.getQueryParameter(\"type\")?.lowercase(Locale.getDefault())) {\n                    \"url\" -> Profile.Type.Url\n                    \"file\" -> Profile.Type.File\n                    else -> Profile.Type.Url\n                }\n                val name = uri.getQueryParameter(\"name\") ?: getString(R.string.new_profile)\n\n                create(type, name).also {\n                    patch(it, name, url, 0)\n                }\n            }\n\n            startActivity(PropertiesActivity::class.intent.setUUID(uuid))\n\n            finish()\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/FilesActivity.kt",
    "content": "@file:Suppress(\"BlockingMethodInNonBlockingContext\")\n\npackage yos.clash.material\n\nimport android.content.Intent\nimport android.net.Uri\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen\nimport com.hjq.permissions.OnPermissionCallback\nimport com.hjq.permissions.Permission\nimport com.hjq.permissions.XXPermissions\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.selects.select\nimport yos.clash.material.common.util.grantPermissions\nimport yos.clash.material.common.util.ticker\nimport yos.clash.material.common.util.uuid\nimport yos.clash.material.design.FilesDesign\nimport yos.clash.material.design.util.showExceptionToast\nimport yos.clash.material.remote.FilesClient\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.util.fileName\nimport yos.clash.material.util.withProfile\nimport java.util.*\nimport java.util.concurrent.TimeUnit\n\nclass FilesActivity : BaseActivity<FilesDesign>() {\n    override suspend fun main() {\n        val uuid = intent.uuid ?: return finish()\n        val profile = withProfile { queryByUUID(uuid) } ?: return finish()\n        val root = uuid.toString()\n\n        val design = FilesDesign(this)\n        val client = FilesClient(this)\n        val stack = Stack<String>()\n\n        design.configurationEditable = profile.type != Profile.Type.Url\n        design.fetch(client, stack, root)\n\n        setContentDesign(design)\n\n        val ticker = ticker(TimeUnit.MINUTES.toMillis(1))\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ActivityStart, Event.ActivityStop -> {\n                            design.fetch(client, stack, root)\n                        }\n\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    try {\n                        when (it) {\n                            FilesDesign.Request.PopStack -> {\n                                if (stack.empty()) {\n                                    finish()\n                                } else {\n                                    stack.pop()\n                                }\n                            }\n\n                            is FilesDesign.Request.OpenDirectory -> {\n                                stack.push(it.file.id)\n                            }\n\n                            is FilesDesign.Request.OpenFile -> {\n                                startActivityForResult(\n                                    ActivityResultContracts.StartActivityForResult(),\n                                    Intent(Intent.ACTION_VIEW).setDataAndType(\n                                        client.buildDocumentUri(it.file.id),\n                                        \"text/plain\"\n                                    ).grantPermissions()\n                                )\n                            }\n\n                            is FilesDesign.Request.DeleteFile -> {\n                                client.deleteDocument(it.file.id)\n                            }\n\n                            is FilesDesign.Request.RenameFile -> {\n                                val newName = design.requestFileName(it.file.name)\n\n                                client.renameDocument(it.file.id, newName)\n                            }\n\n                            is FilesDesign.Request.ImportFile -> {\n                                val hasPermission = XXPermissions.isGranted(\n                                    this@FilesActivity,\n                                    Permission.MANAGE_EXTERNAL_STORAGE\n                                )\n\n                                if (!hasPermission) {\n                                    XXPermissions.with(this@FilesActivity)\n                                        .permission(Permission.MANAGE_EXTERNAL_STORAGE)\n                                        .request(object : OnPermissionCallback {\n                                            override fun onGranted(\n                                                permissions: MutableList<String>,\n                                                allGranted: Boolean\n                                            ) {\n                                                /*if (!allGranted) {\n                                                    Toast.makeText(this@MainActivity, \"部分权限未授予，某些功能可能无法使用\", Toast.LENGTH_SHORT).show()\n                                                }*/\n                                                //成功\n                                                launch(Dispatchers.Main) {\n                                                    val uri: Uri? = startActivityForResult(\n                                                        ActivityResultContracts.GetContent(),\n                                                        \"*/*\"\n                                                    )\n\n                                                    if (uri != null) {\n                                                        if (it.file == null) {\n                                                            val name = design.requestFileName(\n                                                                uri.fileName ?: \"File\"\n                                                            )\n\n                                                            client.importDocument(\n                                                                stack.last(),\n                                                                uri,\n                                                                name\n                                                            )\n                                                        } else {\n                                                            client.copyDocument(\n                                                                it.file!!.id,\n                                                                uri\n                                                            )\n                                                        }\n                                                        design.fetch(client, stack, root)\n                                                    }\n                                                }\n                                            }\n\n                                            override fun onDenied(\n                                                permissions: MutableList<String>,\n                                                doNotAskAgain: Boolean\n                                            ) {\n                                                if (doNotAskAgain) {\n                                                    // 如果是被永久拒绝就跳转到应用权限系统设置页面\n                                                    XXPermissions.startPermissionActivity(\n                                                        this@FilesActivity,\n                                                        permissions\n                                                    )\n                                                }\n                                            }\n                                        })\n                                    /*val granted = startActivityForResult(\n                                        ActivityResultContracts.RequestPermission(),\n                                        Manifest.permission.READ_EXTERNAL_STORAGE,\n                                    )\n\n                                    if (!granted) {\n                                        return@onReceive\n                                    }*/\n                                } else {\n                                    val uri: Uri? = startActivityForResult(\n                                        ActivityResultContracts.GetContent(),\n                                        \"*/*\"\n                                    )\n\n                                    if (uri != null) {\n                                        if (it.file == null) {\n                                            val name = design.requestFileName(\n                                                uri.fileName ?: \"File\"\n                                            )\n                                            client.importDocument(\n                                                stack.last(),\n                                                uri,\n                                                name\n                                            )\n                                        } else {\n                                            client.copyDocument(\n                                                it.file!!.id,\n                                                uri\n                                            )\n                                        }\n                                        design.fetch(client, stack, root)\n                                    }\n                                }\n                            }\n\n                            is FilesDesign.Request.ExportFile -> {\n                                val uri: Uri? = startActivityForResult(\n                                    ActivityResultContracts.CreateDocument(\"text/plain\"),\n                                    it.file.name\n                                )\n\n                                if (uri != null) {\n                                    client.copyDocument(uri, it.file.id)\n                                }\n                            }\n                        }\n                    } catch (e: Exception) {\n                        design.showExceptionToast(e)\n                    }\n\n                    design.fetch(client, stack, root)\n                }\n                if (activityStarted) {\n                    ticker.onReceive {\n                        design.updateElapsed()\n                    }\n                }\n            }\n        }\n    }\n\n    override fun onBackPressed() {\n        design?.requests?.trySend(FilesDesign.Request.PopStack)\n    }\n\n    private suspend fun FilesDesign.fetch(client: FilesClient, stack: Stack<String>, root: String) {\n        val documentId = stack.lastOrNull() ?: root\n        val files = if (stack.empty()) {\n            val list = client.list(documentId)\n            val config = list.firstOrNull { it.id.endsWith(\"config.yaml\") }\n\n            if (config == null || config.size > 0) list else listOf(config)\n        } else {\n            client.list(documentId)\n        }\n\n        swapFiles(files, stack.empty())\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/HelpActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.Intent\nimport yos.clash.material.design.HelpDesign\nimport kotlinx.coroutines.isActive\n\nclass HelpActivity : BaseActivity<HelpDesign>() {\n    override suspend fun main() {\n        val design = HelpDesign(this) {\n            startActivity(Intent(Intent.ACTION_VIEW).setData(it))\n        }\n\n        setContentDesign(design)\n\n        while (isActive) {\n            events.receive()\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/LogcatActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.ServiceConnection\nimport android.net.Uri\nimport android.os.IBinder\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport yos.clash.material.common.compat.startForegroundServiceCompat\nimport yos.clash.material.common.util.fileName\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.ticker\nimport com.github.kr328.clash.core.model.LogMessage\nimport yos.clash.material.design.LogcatDesign\nimport yos.clash.material.design.dialog.withModelProgressBar\nimport yos.clash.material.design.model.LogFile\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.design.util.showExceptionToast\nimport yos.clash.material.log.LogcatFilter\nimport yos.clash.material.log.LogcatReader\nimport yos.clash.material.util.logsDir\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\nimport java.io.OutputStreamWriter\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.suspendCoroutine\n\nclass LogcatActivity : BaseActivity<LogcatDesign>() {\n    private var conn: ServiceConnection? = null\n\n    override suspend fun main() {\n        val fileName = intent?.fileName\n\n        if (fileName != null) {\n            val file = LogFile.parseFromFileName(fileName) ?: return showInvalid()\n\n            return mainLocalFile(file)\n        }\n\n        return mainStreaming()\n    }\n\n    private suspend fun mainLocalFile(file: LogFile) {\n        val messages = try {\n            LogcatReader(this, file).readAll()\n        } catch (e: Exception) {\n            return showInvalid()\n        }\n\n        val design = LogcatDesign(this, false)\n\n        setContentDesign(design)\n\n        design.patchMessages(messages, 0, messages.size)\n\n        while (isActive) {\n            when (design.requests.receive()) {\n                LogcatDesign.Request.Delete -> {\n                    withContext(Dispatchers.IO) {\n                        logsDir.resolve(file.fileName).delete()\n                    }\n\n                    finish()\n                }\n                LogcatDesign.Request.Export -> {\n                    val output = startActivityForResult(\n                        ActivityResultContracts.CreateDocument(\"text/plain\"),\n                        file.fileName\n                    )\n\n                    if (output != null) {\n                        try {\n                            withContext(Dispatchers.IO) {\n                                writeLogTo(messages, file, output)\n                            }\n\n                            design.showToast(R.string.file_exported, ToastDuration.Long)\n                        } catch (e: Exception) {\n                            design.showExceptionToast(e)\n                        }\n                    }\n                }\n                else -> Unit\n            }\n        }\n    }\n\n    private suspend fun mainStreaming() {\n        val design = LogcatDesign(this, true)\n\n        setContentDesign(design)\n\n        startForegroundServiceCompat(LogcatService::class.intent)\n\n        val logcat = bindLogcatService()\n        val ticker = ticker(500)\n\n        var initial = true\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        LogcatDesign.Request.Close -> {\n                            stopService(LogcatService::class.intent)\n\n                            finish()\n                        }\n                        else -> Unit\n                    }\n                }\n                if (activityStarted) {\n                    ticker.onReceive {\n                        val snapshot = logcat.snapshot(initial) ?: return@onReceive\n\n                        design.patchMessages(snapshot.messages, snapshot.removed, snapshot.appended)\n\n                        initial = false\n                    }\n                }\n            }\n        }\n    }\n\n    override fun onDestroy() {\n        conn?.apply(this::unbindService)\n\n        super.onDestroy()\n    }\n\n    private suspend fun bindLogcatService(): LogcatService {\n        return suspendCoroutine { ctx ->\n            bindService(LogcatService::class.intent, object : ServiceConnection {\n                override fun onServiceConnected(name: ComponentName?, service: IBinder?) {\n                    val srv = service!!.queryLocalInterface(\"\") as LogcatService\n\n                    ctx.resume(srv)\n\n                    conn = this\n                }\n\n                override fun onServiceDisconnected(name: ComponentName?) {\n                    conn = null\n                }\n            }, Context.BIND_AUTO_CREATE)\n        }\n    }\n\n    @Suppress(\"BlockingMethodInNonBlockingContext\")\n    private suspend fun writeLogTo(messages: List<LogMessage>, file: LogFile, uri: Uri) {\n        LogcatFilter(OutputStreamWriter(contentResolver.openOutputStream(uri)), this).use {\n            withContext(Dispatchers.Main) {\n                withModelProgressBar {\n                    configure {\n                        isIndeterminate = true\n                        max = messages.size\n                    }\n\n                    withContext(Dispatchers.IO) {\n                        it.writeHeader(file.date)\n\n                        messages.forEachIndexed { idx, msg ->\n                            configure {\n                                isIndeterminate = false\n                                progress = idx\n                            }\n\n                            it.writeMessage(msg)\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private fun showInvalid() {\n        Toast.makeText(this, R.string.invalid_log_file, Toast.LENGTH_LONG).show()\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/LogcatService.kt",
    "content": "package yos.clash.material\n\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.content.ServiceConnection\nimport android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE\nimport android.os.Binder\nimport android.os.Build\nimport android.os.IBinder\nimport android.os.IInterface\nimport androidx.core.app.NotificationChannelCompat\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport com.github.kr328.clash.core.model.LogMessage\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport yos.clash.material.common.compat.getColorCompat\nimport yos.clash.material.common.compat.pendingIntentFlags\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.log.LogcatCache\nimport yos.clash.material.log.LogcatWriter\nimport yos.clash.material.service.RemoteService\nimport yos.clash.material.service.remote.ILogObserver\nimport yos.clash.material.service.remote.IRemoteService\nimport yos.clash.material.service.remote.unwrap\nimport yos.clash.material.util.logsDir\nimport java.io.IOException\n\nclass LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.Default), IInterface {\n    private val cache = LogcatCache()\n\n    private val connection = object : ServiceConnection {\n        override fun onServiceDisconnected(name: ComponentName?) {\n            stopSelf()\n        }\n\n        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {\n            startObserver(service ?: return stopSelf())\n        }\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n\n        running = true\n\n        createNotificationChannel()\n\n        showNotification()\n\n        bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)\n    }\n\n    override fun onDestroy() {\n        cancel()\n\n        unbindService(connection)\n\n        stopForeground(true)\n\n        running = false\n\n        super.onDestroy()\n    }\n\n    override fun onBind(intent: Intent?): IBinder {\n        return this.asBinder()\n    }\n\n    override fun asBinder(): IBinder {\n        return object : Binder() {\n            override fun queryLocalInterface(descriptor: String): IInterface {\n                return this@LogcatService\n            }\n        }\n    }\n\n    suspend fun snapshot(full: Boolean): LogcatCache.Snapshot? {\n        return cache.snapshot(full)\n    }\n\n    private fun startObserver(binder: IBinder) {\n        if (!binder.isBinderAlive)\n            return stopSelf()\n\n        launch(Dispatchers.IO) {\n            val service = binder.unwrap(IRemoteService::class).clash()\n            val channel = Channel<LogMessage>(CACHE_CAPACITY)\n\n            try {\n                logsDir.mkdirs()\n\n                LogcatWriter(this@LogcatService).use {\n                    val observer = object : ILogObserver {\n                        override fun newItem(log: LogMessage) {\n                            channel.trySend(log)\n                        }\n                    }\n\n                    service.setLogObserver(observer)\n\n                    while (isActive) {\n                        val msg = channel.receive()\n\n                        it.appendMessage(msg)\n\n                        cache.append(msg)\n                    }\n                }\n            } catch (e: IOException) {\n                Log.e(\"Write log file: $e\", e)\n            } finally {\n                withContext(NonCancellable) {\n                    if (binder.isBinderAlive) {\n                        service.setLogObserver(null)\n                    }\n\n                    stopSelf()\n                }\n            }\n        }\n    }\n\n    private fun createNotificationChannel() {\n        NotificationManagerCompat.from(this)\n            .createNotificationChannel(\n                NotificationChannelCompat.Builder(\n                    CHANNEL_ID,\n                    NotificationManagerCompat.IMPORTANCE_DEFAULT\n                ).setName(getString(R.string.clash_logcat)).build()\n            )\n    }\n\n    private fun showNotification() {\n        val notification = NotificationCompat\n            .Builder(this, CHANNEL_ID)\n            .setSmallIcon(R.drawable.ic_logo_service)\n            .setColor(getColorCompat(R.color.color_clash_light))\n            .setContentTitle(getString(R.string.clash_logcat))\n            .setContentText(getString(R.string.running))\n            .setContentIntent(\n                PendingIntent.getActivity(\n                    this,\n                    R.id.nf_logcat_status,\n                    LogcatActivity::class.intent\n                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP),\n                    pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT)\n                )\n            )\n            .build()\n\n        // startForeground(R.id.nf_logcat_status, notification)\n        if (Build.VERSION.SDK_INT >= 34) {\n            startForeground(\n                R.id.nf_logcat_status,\n                notification,\n                FOREGROUND_SERVICE_TYPE_SPECIAL_USE\n            )\n        } else {\n            startForeground(R.id.nf_logcat_status, notification)\n        }\n        // Adapt to Android 14\n    }\n\n    companion object {\n        private const val CHANNEL_ID = \"clash_logcat_channel\"\n        private const val CACHE_CAPACITY = 128\n\n        var running: Boolean = false\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/LogsActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.setFileName\nimport yos.clash.material.design.LogsDesign\nimport yos.clash.material.design.model.LogFile\nimport yos.clash.material.util.logsDir\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\n\nclass LogsActivity : BaseActivity<LogsDesign>() {\n    override suspend fun main() {\n        if (LogcatService.running) {\n            return startActivity(LogcatActivity::class.intent)\n        }\n\n        val design = LogsDesign(this)\n\n        setContentDesign(design)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ActivityStart -> {\n                            val files = withContext(Dispatchers.IO) {\n                                loadFiles()\n                            }\n\n                            design.patchLogs(files)\n                        }\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        LogsDesign.Request.StartLogcat -> {\n                            startActivity(LogcatActivity::class.intent)\n\n                            finish()\n                        }\n                        LogsDesign.Request.DeleteAll -> {\n                            if (design.requestDeleteAll()) {\n                                withContext(Dispatchers.IO) {\n                                    deleteAllLogs()\n                                }\n\n                                events.trySend(Event.ActivityStart)\n                            }\n                        }\n                        is LogsDesign.Request.OpenFile -> {\n                            startActivity(LogcatActivity::class.intent.setFileName(it.file.fileName))\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private fun loadFiles(): List<LogFile> {\n        val list = cacheDir.resolve(\"logs\").listFiles()?.toList() ?: emptyList()\n\n        return list.mapNotNull { LogFile.parseFromFileName(it.name) }\n    }\n\n    private fun deleteAllLogs() {\n        logsDir.deleteRecursively()\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/MainActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.Context\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen\nimport com.hjq.permissions.OnPermissionCallback\nimport com.hjq.permissions.Permission\nimport com.hjq.permissions.XXPermissions\nimport yos.clash.material.R\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.ticker\nimport yos.clash.material.design.MainDesign\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.store.TipsStore\nimport yos.clash.material.util.startClashService\nimport yos.clash.material.util.stopClashService\nimport yos.clash.material.util.withClash\nimport yos.clash.material.util.withProfile\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\nimport java.util.concurrent.TimeUnit\n\nclass MainActivity : BaseActivity<MainDesign>() {\n    override suspend fun main() {\n        installSplashScreen()\n        val design = MainDesign(this)\n\n        setContentDesign(design)\n\n        launch(Dispatchers.IO) {\n            showUpdatedTips(design)\n            checkNotificationPermission(design)\n        }\n\n        design.fetch()\n\n        val ticker = ticker(TimeUnit.SECONDS.toMillis(1))\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ActivityStart,\n                        Event.ServiceRecreated,\n                        Event.ClashStop, Event.ClashStart,\n                        Event.ProfileLoaded, Event.ProfileChanged -> design.fetch()\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        MainDesign.Request.ToggleStatus -> {\n                            if (clashRunning)\n                                stopClashService()\n                            else\n                                design.startClash()\n                        }\n                        MainDesign.Request.OpenProxy ->\n                            startActivity(ProxyActivity::class.intent)\n                        MainDesign.Request.OpenProfiles ->\n                            startActivity(ProfilesActivity::class.intent)\n                        MainDesign.Request.OpenProviders ->\n                            startActivity(ProvidersActivity::class.intent)\n                        MainDesign.Request.OpenLogs ->\n                            startActivity(LogsActivity::class.intent)\n                        MainDesign.Request.OpenSettings ->\n                            startActivity(SettingsActivity::class.intent)\n                        MainDesign.Request.OpenHelp ->\n                            startActivity(HelpActivity::class.intent)\n                        MainDesign.Request.OpenAbout ->\n                            design.showAbout(queryAppVersionName())\n                    }\n                }\n                if (clashRunning) {\n                    ticker.onReceive {\n                        design.fetchTraffic()\n                    }\n                }\n            }\n        }\n    }\n\n    private suspend fun showUpdatedTips(design: MainDesign) {\n        val tips = TipsStore(this)\n\n        if (tips.primaryVersion != TipsStore.CURRENT_PRIMARY_VERSION) {\n            tips.primaryVersion = TipsStore.CURRENT_PRIMARY_VERSION\n\n            val pkg = packageManager.getPackageInfo(packageName, 0)\n\n            if (pkg.firstInstallTime != pkg.lastUpdateTime) {\n                design.showUpdatedTips()\n            }\n        }\n    }\n\n    private suspend fun checkNotificationPermission(design: MainDesign) {\n        val permission = XXPermissions.isGranted(this, Permission.POST_NOTIFICATIONS)\n        if (!permission) {\n            design.showPermissionRequest()\n        }\n    }\n\n\n    private suspend fun MainDesign.fetch() {\n        setClashRunning(clashRunning)\n\n        val state = withClash {\n            queryTunnelState()\n        }\n        val providers = withClash {\n            queryProviders()\n        }\n\n        setMode(state.mode)\n        setHasProviders(providers.isNotEmpty())\n\n        withProfile {\n            setProfileName(queryActive()?.name)\n        }\n    }\n\n    private suspend fun MainDesign.fetchTraffic() {\n        withClash {\n            setForwarded(queryTrafficTotal())\n        }\n    }\n\n    private suspend fun MainDesign.startClash() {\n        val active = withProfile { queryActive() }\n\n        if (active == null || !active.imported) {\n            showToast(R.string.no_profile_selected, ToastDuration.Long) {\n                setAction(R.string.profiles) {\n                    startActivity(ProfilesActivity::class.intent)\n                }\n            }\n\n            return\n        }\n\n        val vpnRequest = startClashService()\n\n        try {\n            if (vpnRequest != null) {\n                val result = startActivityForResult(\n                    ActivityResultContracts.StartActivityForResult(),\n                    vpnRequest\n                )\n\n                if (result.resultCode == RESULT_OK)\n                    startClashService()\n            }\n        } catch (e: Exception) {\n            design?.showToast(R.string.unable_to_start_vpn, ToastDuration.Long)\n        }\n    }\n\n    private suspend fun queryAppVersionName(): String {\n        return withContext(Dispatchers.IO) {\n            packageManager.getPackageInfo(packageName, 0).versionName\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/MainApplication.kt",
    "content": "package yos.clash.material\n\nimport android.app.Application\nimport android.content.Context\nimport androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen\nimport yos.clash.material.common.Global\nimport yos.clash.material.common.compat.currentProcessName\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.remote.Remote\nimport yos.clash.material.service.util.sendServiceRecreated\n\n@Suppress(\"unused\")\nclass MainApplication : Application() {\n    override fun attachBaseContext(base: Context?) {\n        super.attachBaseContext(base)\n\n        Global.init(this)\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        val processName = currentProcessName\n\n        Log.d(\"Process $processName started\")\n\n        if (processName == packageName) {\n            Remote.launch()\n        } else {\n            sendServiceRecreated()\n        }\n    }\n\n    fun finalize() {\n        Global.destroy()\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/NetworkSettingsActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.design.NetworkSettingsDesign\nimport yos.clash.material.service.store.ServiceStore\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\n\nclass NetworkSettingsActivity : BaseActivity<NetworkSettingsDesign>() {\n    override suspend fun main() {\n        val design = NetworkSettingsDesign(\n            this,\n            uiStore,\n            ServiceStore(this),\n            clashRunning,\n        )\n\n        setContentDesign(design)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ClashStart, Event.ClashStop, Event.ServiceRecreated ->\n                            recreate()\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        NetworkSettingsDesign.Request.StartAccessControlList ->\n                            startActivity(AccessControlActivity::class.intent)\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/NewProfileActivity.kt",
    "content": "package yos.clash.material\n\nimport android.app.Activity\nimport android.content.ComponentName\nimport android.content.Intent\nimport android.net.Uri\nimport android.provider.Settings\nimport androidx.activity.result.contract.ActivityResultContracts\nimport yos.clash.material.R\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.setUUID\nimport yos.clash.material.design.NewProfileDesign\nimport yos.clash.material.design.model.ProfileProvider\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.util.withProfile\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\nimport java.util.*\n\nclass NewProfileActivity : BaseActivity<NewProfileDesign>() {\n    private val self: NewProfileActivity\n        get() = this\n\n    override suspend fun main() {\n        val design = NewProfileDesign(this)\n\n        design.patchProviders(queryProfileProviders())\n\n        setContentDesign(design)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        is NewProfileDesign.Request.Create -> {\n                            withProfile {\n                                val name = getString(R.string.new_profile)\n\n                                val uuid: UUID? = when (val p = it.provider) {\n                                    is ProfileProvider.File ->\n                                        create(Profile.Type.File, name)\n                                    is ProfileProvider.Url ->\n                                        create(Profile.Type.Url, name)\n                                    is ProfileProvider.External -> {\n                                        val data = p.get()\n\n                                        if (data != null) {\n                                            val (uri, initialName) = data\n\n                                            create(\n                                                Profile.Type.External,\n                                                initialName ?: name,\n                                                uri.toString()\n                                            )\n                                        } else {\n                                            null\n                                        }\n                                    }\n                                }\n\n                                if (uuid != null)\n                                    launchProperties(uuid)\n                            }\n                        }\n                        is NewProfileDesign.Request.OpenDetail -> {\n                            launchAppDetailed(it.provider)\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private fun launchAppDetailed(provider: ProfileProvider.External) {\n        val data = Uri.fromParts(\n            \"package\",\n            provider.intent.component?.packageName ?: return,\n            null\n        )\n\n        startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(data))\n    }\n\n    private suspend fun launchProperties(uuid: UUID) {\n        val r = startActivityForResult(\n            ActivityResultContracts.StartActivityForResult(),\n            PropertiesActivity::class.intent.setUUID(uuid)\n        )\n\n        if (r.resultCode == Activity.RESULT_OK)\n            finish()\n    }\n\n    private suspend fun ProfileProvider.External.get(): Pair<Uri, String?>? {\n        val result = startActivityForResult(\n            ActivityResultContracts.StartActivityForResult(),\n            intent\n        )\n\n        if (result.resultCode != RESULT_OK)\n            return null\n\n        val uri = result.data?.data\n        val name = result.data?.getStringExtra(Intents.EXTRA_NAME)\n\n        if (uri != null) {\n            return uri to name\n        }\n\n        return null\n    }\n\n    private suspend fun queryProfileProviders(): List<ProfileProvider> {\n        return withContext(Dispatchers.IO) {\n            val providers = packageManager.queryIntentActivities(\n                Intent(Intents.ACTION_PROVIDE_URL),\n                0\n            ).map {\n                val activity = it.activityInfo\n\n                val name = activity.applicationInfo.loadLabel(packageManager)\n                val summary = activity.loadLabel(packageManager)\n                val icon = activity.loadIcon(packageManager)\n                val intent = Intent(Intents.ACTION_PROVIDE_URL)\n                    .setComponent(\n                        ComponentName(\n                            activity.packageName,\n                            activity.name\n                        )\n                    )\n\n                ProfileProvider.External(name.toString(), summary.toString(), icon, intent)\n            }\n\n            listOf(ProfileProvider.File(self), ProfileProvider.Url(self)) + providers\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/OverrideSettingsActivity.kt",
    "content": "package yos.clash.material\n\nimport android.content.pm.PackageManager\nimport yos.clash.material.common.compat.getDrawableCompat\nimport yos.clash.material.common.constants.Metadata\nimport com.github.kr328.clash.core.Clash\nimport yos.clash.material.design.OverrideSettingsDesign\nimport yos.clash.material.design.model.AppInfo\nimport yos.clash.material.design.util.toAppInfo\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.util.withClash\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\n\nclass OverrideSettingsActivity : BaseActivity<OverrideSettingsDesign>() {\n    override suspend fun main() {\n        val configuration = withClash { queryOverride(Clash.OverrideSlot.Persist) }\n        val service = ServiceStore(this)\n\n        defer {\n            withClash {\n                patchOverride(Clash.OverrideSlot.Persist, configuration)\n            }\n        }\n\n        val design = OverrideSettingsDesign(\n            this,\n            configuration\n        )\n\n        setContentDesign(design)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        OverrideSettingsDesign.Request.ResetOverride -> {\n                            if (design.requestResetConfirm()) {\n                                defer {\n                                    withClash {\n                                        clearOverride(Clash.OverrideSlot.Persist)\n                                    }\n\n                                    service.sideloadGeoip = \"\"\n                                }\n\n                                finish()\n                            }\n                        }\n                        OverrideSettingsDesign.Request.EditSideloadGeoip -> {\n                            withContext(Dispatchers.IO) {\n                                val list = querySideloadProviders()\n                                val initial = service.sideloadGeoip\n                                val exist = list.any { info -> info.packageName == initial }\n\n                                service.sideloadGeoip =\n                                    design.requestSelectSideload(if (exist) initial else \"\", list)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private fun querySideloadProviders(): List<AppInfo> {\n        val apps = packageManager.getInstalledPackages(PackageManager.GET_META_DATA)\n            .filter {\n                it.applicationInfo.metaData?.containsKey(Metadata.GEOIP_FILE_NAME)\n                    ?: false\n            }\n            .map { it.toAppInfo(packageManager) }\n\n        return listOf(\n            AppInfo(\n                packageName = \"\",\n                label = getString(R.string.use_built_in),\n                icon = getDrawableCompat(R.drawable.ic_baseline_work)!!,\n                installTime = 0,\n                updateDate = 0,\n            )\n        ) + apps\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/ProfilesActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.setUUID\nimport yos.clash.material.common.util.ticker\nimport yos.clash.material.design.ProfilesDesign\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.util.withProfile\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport java.util.concurrent.TimeUnit\n\nclass ProfilesActivity : BaseActivity<ProfilesDesign>() {\n    override suspend fun main() {\n        val design = ProfilesDesign(this)\n\n        setContentDesign(design)\n\n        val ticker = ticker(TimeUnit.MINUTES.toMillis(1))\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ActivityStart, Event.ProfileChanged -> {\n                            design.fetch()\n                        }\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        ProfilesDesign.Request.Create ->\n                            startActivity(NewProfileActivity::class.intent)\n                        ProfilesDesign.Request.UpdateAll ->\n                            withProfile {\n                                queryAll().forEach { p ->\n                                    if (p.imported && p.type != Profile.Type.File)\n                                        update(p.uuid)\n                                }\n                            }\n                        is ProfilesDesign.Request.Update ->\n                            withProfile { update(it.profile.uuid) }\n                        is ProfilesDesign.Request.Delete ->\n                            withProfile { delete(it.profile.uuid) }\n                        is ProfilesDesign.Request.Edit ->\n                            startActivity(PropertiesActivity::class.intent.setUUID(it.profile.uuid))\n                        is ProfilesDesign.Request.Active -> {\n                            withProfile {\n                                if (it.profile.imported)\n                                    setActive(it.profile)\n                                else\n                                    design.requestSave(it.profile)\n                            }\n                        }\n                        is ProfilesDesign.Request.Duplicate -> {\n                            val uuid = withProfile { clone(it.profile.uuid) }\n\n                            startActivity(PropertiesActivity::class.intent.setUUID(uuid))\n                        }\n                    }\n                }\n                if (activityStarted) {\n                    ticker.onReceive {\n                        design.updateElapsed()\n                    }\n                }\n            }\n        }\n    }\n\n    private suspend fun ProfilesDesign.fetch() {\n        withProfile {\n            patchProfiles(queryAll())\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/PropertiesActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.R\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.setUUID\nimport yos.clash.material.common.util.uuid\nimport yos.clash.material.design.PropertiesDesign\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.design.util.showExceptionToast\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.util.withProfile\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.selects.select\n\nclass PropertiesActivity : BaseActivity<PropertiesDesign>() {\n    private var canceled: Boolean = false\n\n    override suspend fun main() {\n        setResult(RESULT_CANCELED)\n\n        val uuid = intent.uuid ?: return finish()\n        val design = PropertiesDesign(this)\n\n        val original = withProfile { queryByUUID(uuid) } ?: return finish()\n\n        design.profile = original\n\n        setContentDesign(design)\n\n        defer {\n            canceled = true\n\n            withProfile { release(uuid) }\n        }\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ActivityStop -> {\n                            val profile = design.profile\n\n                            if (!canceled && profile != original) {\n                                withProfile {\n                                    patch(profile.uuid, profile.name, profile.source, profile.interval)\n                                }\n                            }\n                        }\n                        Event.ServiceRecreated -> {\n                            finish()\n                        }\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        PropertiesDesign.Request.BrowseFiles -> {\n                            startActivity(FilesActivity::class.intent.setUUID(uuid))\n                        }\n                        PropertiesDesign.Request.Commit -> {\n                            design.verifyAndCommit()\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    override fun onBackPressed() {\n        design?.apply {\n            launch {\n                if (!progressing) {\n                    if (requestExitWithoutSaving())\n                        finish()\n                }\n            }\n        } ?: return super.onBackPressed()\n    }\n\n    private suspend fun PropertiesDesign.verifyAndCommit() {\n        when {\n            profile.name.isBlank() -> {\n                showToast(R.string.empty_name, ToastDuration.Long)\n            }\n            profile.type != Profile.Type.File && profile.source.isBlank() -> {\n                showToast(R.string.invalid_url, ToastDuration.Long)\n            }\n            else -> {\n                try {\n                    withProcessing { updateStatus ->\n                        withProfile {\n                            patch(profile.uuid, profile.name, profile.source, profile.interval)\n\n                            coroutineScope {\n                                commit(profile.uuid) {\n                                    launch {\n                                        updateStatus(it)\n                                    }\n                                }\n                            }\n                        }\n                    }\n\n                    setResult(RESULT_OK)\n\n                    finish()\n                } catch (e: Exception) {\n                    showExceptionToast(e)\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/ProvidersActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.R\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.common.util.ticker\nimport yos.clash.material.design.ProvidersDesign\nimport yos.clash.material.design.util.showExceptionToast\nimport yos.clash.material.util.withClash\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.selects.select\nimport java.util.concurrent.TimeUnit\n\nclass ProvidersActivity : BaseActivity<ProvidersDesign>() {\n    override suspend fun main() {\n        val providers = withClash { queryProviders().sorted() }\n        val design = ProvidersDesign(this, providers)\n\n        setContentDesign(design)\n\n        val ticker = ticker(TimeUnit.MINUTES.toMillis(1))\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ProfileLoaded -> {\n                            val newList = withClash { queryProviders().sorted() }\n\n                            if (newList != providers) {\n                                startActivity(ProvidersActivity::class.intent)\n\n                                finish()\n                            }\n                        }\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        is ProvidersDesign.Request.Update -> {\n                            launch {\n                                try {\n                                    withClash {\n                                        updateProvider(it.provider.type, it.provider.name)\n                                    }\n\n                                    design.notifyChanged(it.index)\n                                } catch (e: Exception) {\n                                    design.showExceptionToast(\n                                        getString(\n                                            R.string.format_update_provider_failure,\n                                            it.provider.name,\n                                            e.message\n                                        )\n                                    )\n\n                                    design.notifyUpdated(it.index)\n                                }\n                            }\n                        }\n                    }\n                }\n                if (activityStarted) {\n                    ticker.onReceive {\n                        design.updateElapsed()\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/ProxyActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.common.util.intent\nimport com.github.kr328.clash.core.Clash\nimport com.github.kr328.clash.core.model.Proxy\nimport yos.clash.material.design.ProxyDesign\nimport yos.clash.material.design.model.ProxyState\nimport yos.clash.material.store.TipsStore\nimport yos.clash.material.util.withClash\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.sync.Semaphore\nimport kotlinx.coroutines.sync.withPermit\nimport java.util.concurrent.TimeUnit\n\nclass ProxyActivity : BaseActivity<ProxyDesign>() {\n    override suspend fun main() {\n        val mode = withClash { queryOverride(Clash.OverrideSlot.Session).mode }\n        val names = withClash { queryProxyGroupNames(uiStore.proxyExcludeNotSelectable) }\n        val states = List(names.size) { ProxyState(\"?\") }\n        val unorderedStates = names.indices.map { names[it] to states[it] }.toMap()\n        val reloadLock = Semaphore(10)\n        val tips = TipsStore(this)\n\n        val design = ProxyDesign(\n            this,\n            mode,\n            names,\n            uiStore\n        )\n\n        setContentDesign(design)\n\n        launch(Dispatchers.IO) {\n            val pkg = packageManager.getPackageInfo(packageName, 0)\n            val validate = System.currentTimeMillis() - pkg.firstInstallTime > TimeUnit.DAYS.toMillis(5)\n\n            if (tips.requestDonate && validate) {\n                tips.requestDonate = false\n\n                design.requestDonate()\n            }\n        }\n\n        design.requests.send(ProxyDesign.Request.ReloadAll)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n                    when (it) {\n                        Event.ProfileLoaded -> {\n                            val newNames = withClash {\n                                queryProxyGroupNames(uiStore.proxyExcludeNotSelectable)\n                            }\n\n                            if (newNames != names) {\n                                startActivity(ProxyActivity::class.intent)\n\n                                finish()\n                            }\n                        }\n                        else -> Unit\n                    }\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        ProxyDesign.Request.ReLaunch -> {\n                            startActivity(ProxyActivity::class.intent)\n\n                            finish()\n                        }\n                        ProxyDesign.Request.ReloadAll -> {\n                            names.indices.forEach { idx ->\n                                design.requests.trySend(ProxyDesign.Request.Reload(idx))\n                            }\n                        }\n                        is ProxyDesign.Request.Reload -> {\n                            launch {\n                                val group = reloadLock.withPermit {\n                                    withClash {\n                                        queryProxyGroup(names[it.index], uiStore.proxySort)\n                                    }\n                                }\n                                val state = states[it.index]\n\n                                state.now = group.now\n\n                                design.updateGroup(\n                                    it.index,\n                                    group.proxies,\n                                    group.type == Proxy.Type.Selector,\n                                    state,\n                                    unorderedStates\n                                )\n                            }\n                        }\n                        is ProxyDesign.Request.Select -> {\n                            withClash {\n                                patchSelector(names[it.index], it.name)\n\n                                states[it.index].now = it.name\n                            }\n\n                            design.requestRedrawVisible()\n                        }\n                        is ProxyDesign.Request.UrlTest -> {\n                            launch {\n                                withClash {\n                                    healthCheck(names[it.index])\n                                }\n\n                                design.requests.send(ProxyDesign.Request.Reload(it.index))\n                            }\n                        }\n                        is ProxyDesign.Request.PatchMode -> {\n                            design.showModeSwitchTips()\n\n                            withClash {\n                                val o = queryOverride(Clash.OverrideSlot.Session)\n\n                                o.mode = it.mode\n\n                                patchOverride(Clash.OverrideSlot.Session, o)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/RestartReceiver.kt",
    "content": "package yos.clash.material\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport yos.clash.material.service.StatusProvider\nimport yos.clash.material.util.startClashService\n\nclass RestartReceiver : BroadcastReceiver() {\n    override fun onReceive(context: Context, intent: Intent) {\n        when (intent.action) {\n            Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED -> {\n                if (StatusProvider.shouldStartClashOnBoot)\n                    context.startClashService()\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/SettingsActivity.kt",
    "content": "package yos.clash.material\n\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.design.SettingsDesign\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\n\nclass SettingsActivity : BaseActivity<SettingsDesign>() {\n    override suspend fun main() {\n        val design = SettingsDesign(this)\n\n        setContentDesign(design)\n\n        while (isActive) {\n            select<Unit> {\n                events.onReceive {\n\n                }\n                design.requests.onReceive {\n                    when (it) {\n                        SettingsDesign.Request.StartApp ->\n                            startActivity(AppSettingsActivity::class.intent)\n                        SettingsDesign.Request.StartNetwork ->\n                            startActivity(NetworkSettingsActivity::class.intent)\n                        SettingsDesign.Request.StartOverride ->\n                            startActivity(OverrideSettingsActivity::class.intent)\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/TileService.kt",
    "content": "package yos.clash.material\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.graphics.drawable.Icon\nimport android.os.Build\nimport android.service.quicksettings.Tile\nimport android.service.quicksettings.TileService\nimport androidx.annotation.RequiresApi\nimport yos.clash.material.R\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.constants.Permissions\nimport yos.clash.material.remote.StatusClient\nimport yos.clash.material.util.startClashService\nimport yos.clash.material.util.stopClashService\n\n@RequiresApi(Build.VERSION_CODES.N)\nclass TileService : TileService() {\n    private var currentProfile = \"\"\n    private var clashRunning = false\n\n    override fun onClick() {\n        val tile = qsTile ?: return\n\n        when (tile.state) {\n            Tile.STATE_INACTIVE -> {\n                startClashService()\n            }\n            Tile.STATE_ACTIVE -> {\n                stopClashService()\n            }\n        }\n    }\n\n    override fun onStartListening() {\n        super.onStartListening()\n\n        registerReceiver(\n            receiver,\n            IntentFilter().apply {\n                addAction(Intents.ACTION_CLASH_STARTED)\n                addAction(Intents.ACTION_CLASH_STOPPED)\n                addAction(Intents.ACTION_PROFILE_LOADED)\n                addAction(Intents.ACTION_SERVICE_RECREATED)\n            },\n            Permissions.RECEIVE_SELF_BROADCASTS,\n            null\n        )\n\n        val name = StatusClient(this).currentProfile()\n\n        clashRunning = name != null\n        currentProfile = name ?: \"\"\n\n        updateTile()\n    }\n\n    override fun onStopListening() {\n        super.onStopListening()\n\n        unregisterReceiver(receiver)\n    }\n\n    private fun updateTile() {\n        val tile = qsTile ?: return\n\n        tile.state = if (clashRunning)\n            Tile.STATE_ACTIVE\n        else\n            Tile.STATE_INACTIVE\n\n        tile.label = if (currentProfile.isEmpty())\n            getText(R.string.launch_name)\n        else\n            currentProfile\n\n        tile.icon = Icon.createWithResource(this, R.drawable.ic_logo_service)\n\n        tile.updateTile()\n    }\n\n    private val receiver = object : BroadcastReceiver() {\n        override fun onReceive(context: Context?, intent: Intent?) {\n            when (intent?.action) {\n                Intents.ACTION_CLASH_STARTED -> {\n                    clashRunning = true\n\n                    currentProfile = \"\"\n                }\n                Intents.ACTION_CLASH_STOPPED, Intents.ACTION_SERVICE_RECREATED -> {\n                    clashRunning = false\n\n                    currentProfile = \"\"\n                }\n                Intents.ACTION_PROFILE_LOADED -> {\n                    currentProfile = StatusClient(this@TileService).currentProfile() ?: \"\"\n                }\n            }\n\n            updateTile()\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/log/LogcatCache.kt",
    "content": "package yos.clash.material.log\n\nimport androidx.collection.CircularArray\nimport com.github.kr328.clash.core.model.LogMessage\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\n\nclass LogcatCache {\n    data class Snapshot(val messages: List<LogMessage>, val removed: Int, val appended: Int)\n\n    private val array = CircularArray<LogMessage>(CAPACITY)\n    private val lock = Mutex()\n\n    private var removed: Int = 0\n    private var appended: Int = 0\n\n    suspend fun append(msg: LogMessage) {\n        lock.withLock {\n            if (array.size() >= CAPACITY) {\n                array.removeFromStart(1)\n\n                removed++\n                appended--\n            }\n\n            array.addLast(msg)\n\n            appended++\n        }\n    }\n\n    suspend fun snapshot(full: Boolean): Snapshot? {\n        return lock.withLock {\n            if (!full && removed == 0 && appended == 0) {\n                return@withLock null\n            }\n\n            Snapshot(\n                List(array.size()) { array[it] },\n                removed,\n                if (full) array.size() + appended else appended\n            ).also {\n                removed = 0\n                appended = 0\n            }\n        }\n    }\n\n    companion object {\n        const val CAPACITY = 128\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/log/LogcatFilter.kt",
    "content": "package yos.clash.material.log\n\nimport android.content.Context\nimport com.github.kr328.clash.core.model.LogMessage\nimport yos.clash.material.design.util.format\nimport java.io.BufferedWriter\nimport java.io.Writer\nimport java.util.*\n\nclass LogcatFilter(output: Writer, private val context: Context) : BufferedWriter(output) {\n    fun writeHeader(time: Date) {\n        appendLine(\"# Capture on ${time.format(context)}\")\n    }\n\n    fun writeMessage(message: LogMessage) {\n        val time = message.time.format(context, includeDate = false)\n        val level = message.level.name\n\n        appendLine(FORMAT.format(time, level, message.message))\n    }\n\n    companion object {\n        private const val FORMAT = \"%12s %7s: %s\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/log/LogcatReader.kt",
    "content": "package yos.clash.material.log\n\nimport android.content.Context\nimport com.github.kr328.clash.core.model.LogMessage\nimport yos.clash.material.design.model.LogFile\nimport yos.clash.material.util.logsDir\nimport java.io.BufferedReader\nimport java.io.FileReader\nimport java.util.*\n\nclass LogcatReader(context: Context, file: LogFile) : AutoCloseable {\n    private val reader = BufferedReader(FileReader(context.logsDir.resolve(file.fileName)))\n\n    override fun close() {\n        reader.close()\n    }\n\n    fun readAll(): List<LogMessage> {\n        return reader.lineSequence()\n            .map { it.trim() }\n            .filter { !it.startsWith(\"#\") }\n            .map { it.split(\":\", limit = 3) }\n            .map {\n                LogMessage(\n                    time = Date(it[0].toLong()),\n                    level = LogMessage.Level.valueOf(it[1]),\n                    message = it[2]\n                )\n            }\n            .toList()\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/log/LogcatWriter.kt",
    "content": "package yos.clash.material.log\n\nimport android.content.Context\nimport com.github.kr328.clash.core.model.LogMessage\nimport yos.clash.material.design.model.LogFile\nimport yos.clash.material.util.logsDir\nimport java.io.BufferedWriter\nimport java.io.FileWriter\n\nclass LogcatWriter(context: Context) : AutoCloseable {\n    private val file = LogFile.generate()\n    private val writer = BufferedWriter(FileWriter(context.logsDir.resolve(file.fileName)))\n\n    override fun close() {\n        writer.close()\n    }\n\n    fun appendMessage(message: LogMessage) {\n        writer.appendLine(FORMAT.format(message.time.time, message.level.name, message.message))\n    }\n\n    companion object {\n        private const val FORMAT = \"%d:%s:%s\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/log/SystemLogcat.kt",
    "content": "package yos.clash.material.log\n\nobject SystemLogcat {\n    private val command = arrayOf(\n        \"logcat\",\n        \"-d\",\n        \"-s\",\n        \"Go\",\n        \"DEBUG\",\n        \"AndroidRuntime\",\n        \"ClashForAndroid\",\n        \"LwIP\",\n    )\n\n    fun dumpCrash(): String {\n        return try {\n            val process = Runtime.getRuntime().exec(command)\n\n            val result = process.inputStream.use { stream ->\n                stream.reader().readLines()\n                    .filterNot { it.startsWith(\"------\") }\n                    .joinToString(\"\\n\")\n            }\n\n            process.waitFor()\n\n            result.trim()\n        } catch (e: Exception) {\n            \"\"\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/remote/Broadcasts.kt",
    "content": "package yos.clash.material.remote\n\nimport android.app.Application\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.log.Log\n\nclass Broadcasts(private val context: Application) {\n    interface Observer {\n        fun onServiceRecreated()\n        fun onStarted()\n        fun onStopped(cause: String?)\n        fun onProfileChanged()\n        fun onProfileLoaded()\n    }\n\n    var clashRunning: Boolean = false\n\n    private var registered = false\n    private val receivers = mutableListOf<Observer>()\n    private val broadcastReceiver = object : BroadcastReceiver() {\n        override fun onReceive(context: Context?, intent: Intent?) {\n            if (intent?.`package` != context?.packageName)\n                return\n\n            when (intent?.action) {\n                Intents.ACTION_SERVICE_RECREATED -> {\n                    clashRunning = false\n\n                    receivers.forEach {\n                        it.onServiceRecreated()\n                    }\n                }\n                Intents.ACTION_CLASH_STARTED -> {\n                    clashRunning = true\n\n                    receivers.forEach {\n                        it.onStarted()\n                    }\n                }\n                Intents.ACTION_CLASH_STOPPED -> {\n                    clashRunning = false\n\n                    receivers.forEach {\n                        it.onStopped(intent.getStringExtra(Intents.EXTRA_STOP_REASON))\n                    }\n                }\n                Intents.ACTION_PROFILE_CHANGED ->\n                    receivers.forEach {\n                        it.onProfileChanged()\n                    }\n                Intents.ACTION_PROFILE_LOADED -> {\n                    receivers.forEach {\n                        it.onProfileLoaded()\n                    }\n                }\n            }\n        }\n    }\n\n    fun addObserver(observer: Observer) {\n        receivers.add(observer)\n    }\n\n    fun removeObserver(observer: Observer) {\n        receivers.remove(observer)\n    }\n\n    fun register() {\n        if (registered)\n            return\n\n        try {\n            context.registerReceiver(broadcastReceiver, IntentFilter().apply {\n                addAction(Intents.ACTION_SERVICE_RECREATED)\n                addAction(Intents.ACTION_CLASH_STARTED)\n                addAction(Intents.ACTION_CLASH_STOPPED)\n                addAction(Intents.ACTION_PROFILE_CHANGED)\n                addAction(Intents.ACTION_PROFILE_LOADED)\n            })\n\n            clashRunning = StatusClient(context).currentProfile() != null\n        } catch (e: Exception) {\n            Log.w(\"Register global receiver: $e\", e)\n        }\n    }\n\n    fun unregister() {\n        if (!registered)\n            return\n\n        try {\n            context.unregisterReceiver(broadcastReceiver)\n\n            clashRunning = false\n        } catch (e: Exception) {\n            Log.w(\"Unregister global receiver: $e\", e)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/remote/FilesClient.kt",
    "content": "@file:Suppress(\"BlockingMethodInNonBlockingContext\")\n\npackage yos.clash.material.remote\n\nimport android.content.Context\nimport android.net.Uri\nimport yos.clash.material.common.constants.Authorities\nimport yos.clash.material.design.model.File\nimport yos.clash.material.util.copyContentTo\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport android.provider.DocumentsContract as DC\n\nclass FilesClient(private val context: Context) {\n    suspend fun list(parentDocumentId: String): List<File> = withContext(Dispatchers.IO) {\n        val uri = DC.buildChildDocumentsUri(Authorities.FILES_PROVIDER, parentDocumentId)\n\n        context.contentResolver.query(uri, FilesProjection, null, null, null)?.use { cursor ->\n            val idIndex = cursor.getColumnIndex(DC.Document.COLUMN_DOCUMENT_ID)\n            val nameIndex = cursor.getColumnIndex(DC.Document.COLUMN_DISPLAY_NAME)\n            val sizeIndex = cursor.getColumnIndex(DC.Document.COLUMN_SIZE)\n            val lastModified = cursor.getColumnIndex(DC.Document.COLUMN_LAST_MODIFIED)\n            val mimeTypeIndex = cursor.getColumnIndex(DC.Document.COLUMN_MIME_TYPE)\n\n            cursor.moveToFirst()\n\n            List(cursor.count) {\n                File(\n                    id = cursor.getString(idIndex),\n                    name = cursor.getString(nameIndex),\n                    size = cursor.getLong(sizeIndex),\n                    lastModified = cursor.getLong(lastModified),\n                    isDirectory = cursor.getString(mimeTypeIndex) == DC.Document.MIME_TYPE_DIR,\n                ).also {\n                    cursor.moveToNext()\n                }\n            }.sortedWith(compareBy({ !it.isDirectory }, { it.name }))\n        } ?: emptyList()\n    }\n\n    suspend fun renameDocument(documentId: String, name: String) = withContext(Dispatchers.IO) {\n        val uri = buildDocumentUri(documentId)\n\n        DC.renameDocument(context.contentResolver, uri, name)\n    }\n\n    suspend fun deleteDocument(documentId: String) = withContext(Dispatchers.IO) {\n        val uri = buildDocumentUri(documentId)\n\n        DC.deleteDocument(context.contentResolver, uri)\n    }\n\n    suspend fun importDocument(\n        parentDocumentId: String,\n        source: Uri,\n        name: String\n    ) = withContext(Dispatchers.IO) {\n        val target = buildDocumentUri(\"$parentDocumentId/$name\")\n\n        context.contentResolver.copyContentTo(source, target)\n    }\n\n    suspend fun copyDocument(\n        documentId: String,\n        source: Uri\n    ) {\n        val target = buildDocumentUri(documentId)\n\n        context.contentResolver.copyContentTo(source, target)\n    }\n\n    suspend fun copyDocument(\n        target: Uri,\n        documentId: String\n    ) {\n        val source = buildDocumentUri(documentId)\n\n        context.contentResolver.copyContentTo(source, target)\n    }\n\n    fun buildDocumentUri(documentId: String): Uri {\n        return DC.buildDocumentUri(Authorities.FILES_PROVIDER, documentId)\n    }\n\n    companion object {\n        private val FilesProjection = arrayOf(\n            DC.Document.COLUMN_DOCUMENT_ID,\n            DC.Document.COLUMN_DISPLAY_NAME,\n            DC.Document.COLUMN_SIZE,\n            DC.Document.COLUMN_LAST_MODIFIED,\n            DC.Document.COLUMN_MIME_TYPE,\n        )\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/remote/Remote.kt",
    "content": "package yos.clash.material.remote\n\nimport android.content.Context\nimport android.content.Intent\nimport yos.clash.material.ApkBrokenActivity\nimport yos.clash.material.AppCrashedActivity\nimport yos.clash.material.common.Global\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.store.AppStore\nimport yos.clash.material.util.ApplicationObserver\nimport yos.clash.material.util.verifyApk\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.launch\n\nobject Remote {\n    val broadcasts: Broadcasts = Broadcasts(Global.application)\n    val service: Service = Service(Global.application) {\n        ApplicationObserver.createdActivities.forEach { it.finish() }\n\n        val intent = AppCrashedActivity::class.intent\n            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n\n        Global.application.startActivity(intent)\n    }\n\n    private val visible = Channel<Boolean>(Channel.CONFLATED)\n\n    fun launch() {\n        ApplicationObserver.attach(Global.application)\n\n        ApplicationObserver.onVisibleChanged { visible.trySend(it) }\n\n        Global.launch(Dispatchers.IO) {\n            run()\n        }\n    }\n\n    private suspend fun run() {\n        val context = Global.application\n        val store = AppStore(context)\n        val updatedAt = getLastUpdated(context)\n\n        if (store.updatedAt != updatedAt) {\n            if (!context.verifyApk()) {\n                ApplicationObserver.createdActivities.forEach { it.finish() }\n\n                val intent = ApkBrokenActivity::class.intent\n                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n\n                return context.startActivity(intent)\n            } else {\n                store.updatedAt = updatedAt\n            }\n        }\n\n        while (true) {\n            if (visible.receive()) {\n                service.bind()\n                broadcasts.register()\n            } else {\n                service.unbind()\n                broadcasts.unregister()\n            }\n        }\n    }\n\n    private fun getLastUpdated(context: Context): Long {\n        return context.packageManager.getPackageInfo(context.packageName, 0).lastUpdateTime\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/remote/Resource.kt",
    "content": "package yos.clash.material.remote\n\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlin.coroutines.resume\n\nclass Resource<T> {\n    private interface Callback<T> {\n        fun accept(value: T)\n    }\n\n    private val pending: MutableSet<Callback<T>> = mutableSetOf()\n\n    private var value: T? = null\n\n    suspend fun get(): T {\n        return suspendCancellableCoroutine { ctx ->\n            val callback = object : Callback<T> {\n                override fun accept(value: T) {\n                    ctx.resume(value)\n                }\n            }\n\n            ctx.invokeOnCancellation {\n                cancel(callback)\n            }\n\n            get(callback)\n        }\n    }\n\n    fun set(v: T?) {\n        setAndNotify(v)\n    }\n\n    fun reset(v: T) {\n        resetIfMatched(v)\n    }\n\n    @Synchronized\n    private fun get(callback: Callback<T>) {\n        val v = value\n\n        if (v == null) {\n            pending.add(callback)\n        } else {\n            callback.accept(v)\n        }\n    }\n\n    @Synchronized\n    private fun setAndNotify(value: T?) {\n        this.value = value\n\n        if (value != null) {\n            pending.forEach {\n                it.accept(value)\n            }\n\n            pending.clear()\n        }\n    }\n\n    @Synchronized\n    private fun resetIfMatched(value: T) {\n        if (this.value === value) {\n            this.value = null\n        }\n    }\n\n    @Synchronized\n    private fun cancel(callback: Callback<T>) {\n        pending.remove(callback)\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/remote/Service.kt",
    "content": "package yos.clash.material.remote\n\nimport android.app.Application\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.ServiceConnection\nimport android.os.IBinder\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.service.RemoteService\nimport yos.clash.material.service.remote.IRemoteService\nimport yos.clash.material.service.remote.unwrap\nimport yos.clash.material.util.unbindServiceSilent\nimport java.util.concurrent.TimeUnit\n\nclass Service(private val context: Application, val crashed: () -> Unit) {\n    val remote = Resource<IRemoteService>()\n\n    private val connection = object : ServiceConnection {\n        private var lastCrashed: Long = -1\n\n        override fun onServiceConnected(name: ComponentName?, service: IBinder) {\n            remote.set(service.unwrap(IRemoteService::class))\n        }\n\n        override fun onServiceDisconnected(name: ComponentName?) {\n            remote.set(null)\n\n            if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {\n                unbind()\n\n                crashed()\n            }\n\n            lastCrashed = System.currentTimeMillis()\n\n            Log.w(\"RemoteManager crashed\")\n        }\n    }\n\n    fun bind() {\n        try {\n            context.bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)\n        } catch (e: Exception) {\n            unbind()\n\n            crashed()\n        }\n    }\n\n    fun unbind() {\n        context.unbindServiceSilent(connection)\n\n        remote.set(null)\n    }\n\n    companion object {\n        private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/remote/StatusClient.kt",
    "content": "package yos.clash.material.remote\n\nimport android.content.Context\nimport android.net.Uri\nimport yos.clash.material.common.constants.Authorities\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.service.StatusProvider\n\nclass StatusClient(private val context: Context) {\n    private val uri: Uri\n        get() {\n            return Uri.Builder()\n                .scheme(\"content\")\n                .authority(Authorities.STATUS_PROVIDER)\n                .build()\n        }\n\n    fun currentProfile(): String? {\n        return try {\n            val result = context.contentResolver.call(\n                uri,\n                StatusProvider.METHOD_CURRENT_PROFILE,\n                null,\n                null\n            )\n\n            result?.getString(\"name\")\n        } catch (e: Exception) {\n            Log.w(\"Query current profile: $e\", e)\n\n            null\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/store/AppStore.kt",
    "content": "package yos.clash.material.store\n\nimport android.content.Context\nimport yos.clash.material.common.store.Store\nimport yos.clash.material.common.store.asStoreProvider\n\nclass AppStore(context: Context) {\n    private val store = Store(\n        context\n            .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)\n            .asStoreProvider()\n    )\n\n    var updatedAt: Long by store.long(\n        key = \"updated_at\",\n        defaultValue = -1,\n    )\n\n    companion object {\n        private const val FILE_NAME = \"app\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/store/TipsStore.kt",
    "content": "package yos.clash.material.store\n\nimport android.content.Context\nimport yos.clash.material.common.store.Store\nimport yos.clash.material.common.store.asStoreProvider\n\nclass TipsStore(context: Context) {\n    private val store = Store(\n        context\n            .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)\n            .asStoreProvider()\n    )\n\n    var requestDonate: Boolean by store.boolean(\n        key = \"request_donate\",\n        defaultValue = true,\n    )\n\n    var primaryVersion: Int by store.int(\n        key = \"primary_version\",\n        defaultValue = -1,\n    )\n\n    companion object {\n        const val CURRENT_PRIMARY_VERSION = 1\n\n        private const val FILE_NAME = \"tips\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Activity.kt",
    "content": "@file:Suppress(\"LeakingThis\")\n\npackage yos.clash.material.util\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.LifecycleRegistry\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.withContext\n\nclass ActivityResultLifecycle : LifecycleOwner {\n    private val lifecycle = LifecycleRegistry(this)\n\n    init {\n        lifecycle.currentState = Lifecycle.State.INITIALIZED\n    }\n\n    /*override fun getLifecycle(): Lifecycle {\n        return lifecycle\n    }*/\n\n    suspend fun <T> use(block: suspend (lifecycle: ActivityResultLifecycle, start: () -> Unit) -> T): T {\n        return try {\n            markCreated()\n\n            block(this, this::markStarted)\n        } finally {\n            withContext(NonCancellable) {\n                markDestroy()\n            }\n        }\n    }\n\n    private fun markCreated() {\n        lifecycle.currentState = Lifecycle.State.CREATED\n    }\n\n    private fun markStarted() {\n        lifecycle.currentState = Lifecycle.State.STARTED\n        lifecycle.currentState = Lifecycle.State.RESUMED\n    }\n\n    private fun markDestroy() {\n        lifecycle.currentState = Lifecycle.State.DESTROYED\n    }\n\n    override fun getLifecycle(): Lifecycle {\n        return lifecycle\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Application.kt",
    "content": "package yos.clash.material.util\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Context\nimport android.os.Build\nimport android.os.Bundle\nimport java.io.File\nimport java.util.zip.ZipFile\n\nobject ApplicationObserver {\n    private val activities: MutableSet<Activity> = mutableSetOf()\n\n    private var visibleChanged: (Boolean) -> Unit = {}\n\n    private var appVisible = false\n        private set(value) {\n            if (field != value) {\n                field = value\n\n                visibleChanged(value)\n            }\n        }\n\n    val createdActivities: Set<Activity>\n        get() = activities\n\n    private val activityObserver = object : Application.ActivityLifecycleCallbacks {\n        @Synchronized\n        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n            activities.add(activity)\n\n            appVisible = true\n        }\n\n        @Synchronized\n        override fun onActivityDestroyed(activity: Activity) {\n            activities.remove(activity)\n\n            appVisible = activities.isNotEmpty()\n        }\n\n        override fun onActivityStarted(activity: Activity) {}\n        override fun onActivityStopped(activity: Activity) {}\n        override fun onActivityPaused(activity: Activity) {}\n        override fun onActivityResumed(activity: Activity) {}\n        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}\n    }\n\n    fun onVisibleChanged(visibleChanged: (Boolean) -> Unit) {\n        ApplicationObserver.visibleChanged = visibleChanged\n    }\n\n    fun attach(application: Application) {\n        application.registerActivityLifecycleCallbacks(activityObserver)\n    }\n}\n\nfun Context.verifyApk(): Boolean {\n    return try {\n        val info = applicationInfo\n        val sources = info.splitSourceDirs ?: arrayOf(info.sourceDir) ?: return false\n\n        val regexNativeLibrary = Regex(\"lib/(\\\\S+)/libclash.so\")\n        val availableAbi = Build.SUPPORTED_ABIS.toSet()\n        val apkAbi = sources\n            .asSequence()\n            .filter { File(it).exists() }\n            .flatMap { ZipFile(it).entries().asSequence() }\n            .mapNotNull { regexNativeLibrary.matchEntire(it.name) }\n            .mapNotNull { it.groups[1]?.value }\n            .toSet()\n\n        availableAbi.intersect(apkAbi).isNotEmpty()\n    } catch (e: Exception) {\n        false\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Clash.kt",
    "content": "package yos.clash.material.util\n\nimport android.content.Context\nimport android.content.Intent\nimport android.net.VpnService\nimport yos.clash.material.common.compat.startForegroundServiceCompat\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.util.intent\nimport yos.clash.material.design.store.UiStore\nimport yos.clash.material.service.ClashService\nimport yos.clash.material.service.TunService\nimport yos.clash.material.service.util.sendBroadcastSelf\n\nfun Context.startClashService(): Intent? {\n    val startTun = UiStore(this).enableVpn\n\n    if (startTun) {\n        val vpnRequest = VpnService.prepare(this)\n        if (vpnRequest != null)\n            return vpnRequest\n\n        startForegroundServiceCompat(TunService::class.intent)\n    } else {\n        startForegroundServiceCompat(ClashService::class.intent)\n    }\n\n    return null\n}\n\nfun Context.stopClashService() {\n    sendBroadcastSelf(Intent(Intents.ACTION_CLASH_REQUEST_STOP))\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Content.kt",
    "content": "package yos.clash.material.util\n\nimport android.content.ContentResolver\nimport android.net.Uri\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.io.FileNotFoundException\n\nprivate fun fileNotFound(file: Uri): FileNotFoundException {\n    return FileNotFoundException(\"$file not found\")\n}\n\n@Suppress(\"BlockingMethodInNonBlockingContext\")\nsuspend fun ContentResolver.copyContentTo(\n    source: Uri,\n    target: Uri\n) {\n    withContext(Dispatchers.IO) {\n        (openInputStream(source) ?: throw fileNotFound(source)).use { input ->\n            (openOutputStream(target, \"rwt\") ?: throw fileNotFound(target)).use { output ->\n                input.copyTo(output)\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Files.kt",
    "content": "package yos.clash.material.util\n\nimport android.content.Context\nimport java.io.File\n\nval Context.logsDir: File\n    get() = cacheDir.resolve(\"logs\")"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Remote.kt",
    "content": "package yos.clash.material.util\n\nimport android.os.DeadObjectException\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.remote.Remote\nimport yos.clash.material.service.remote.IClashManager\nimport yos.clash.material.service.remote.IProfileManager\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport kotlin.coroutines.CoroutineContext\n\nsuspend fun <T> withClash(\n    context: CoroutineContext = Dispatchers.IO,\n    block: suspend IClashManager.() -> T\n): T {\n    while (true) {\n        val remote = Remote.service.remote.get()\n        val client = remote.clash()\n\n        try {\n            return withContext(context) { client.block() }\n        } catch (e: DeadObjectException) {\n            Log.w(\"Remote services panic\")\n\n            Remote.service.remote.reset(remote)\n        }\n    }\n}\n\nsuspend fun <T> withProfile(\n    context: CoroutineContext = Dispatchers.IO,\n    block: suspend IProfileManager.() -> T\n): T {\n    while (true) {\n        val remote = Remote.service.remote.get()\n        val client = remote.profile()\n\n        try {\n            return withContext(context) { client.block() }\n        } catch (e: DeadObjectException) {\n            Log.w(\"Remote services panic\")\n\n            Remote.service.remote.reset(remote)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Service.kt",
    "content": "package yos.clash.material.util\n\nimport android.content.Context\nimport android.content.ServiceConnection\n\nfun Context.unbindServiceSilent(connection: ServiceConnection) {\n    try {\n        unbindService(connection)\n    } catch (e: Exception) {\n        // ignore\n    }\n}"
  },
  {
    "path": "app/src/main/java/yos/clash/material/util/Uri.kt",
    "content": "package yos.clash.material.util\n\nimport android.net.Uri\n\nval Uri.fileName: String?\n    get() = schemeSpecificPart.split(\"/\").lastOrNull()"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"406.92642\"\n    android:viewportHeight=\"406.92642\">\n    <group\n        android:translateX=\"103.4632\"\n        android:translateY=\"103.4632\">\n        <path\n            android:fillColor=\"@color/color_clash_light\"\n            android:pathData=\"M37.7,168.1C61.1-35,58,13.8,84.6,43.8c13.5-1.5,27.2-3.5,40.8-0.7c2.6,0.1,5.4,1.9,7.7,0.2\n\t\tc4.1-6.3,7.4-13.5,11.4-20c12.2-24.4,12.8,19.5,15.5,26.5c5.2,33,10.9,64.5,14.7,97.6c0.3,10.7,5.7,32.9,1.1,33.6\n\t\tc-49.4,5.2-147.7,3.9-160.8-6.3c-15.8-10.5-15.2-35.3,2-43.7c3.6-2,10.7-3.7,11.9,0.7c-2.4,5-8.8,5.8-12.1,11.2\n\t\tC7.2,158.9,23.6,168.9,37.7,168.1z M78.4,74c-10.4,0.5-9.6,14.9-0.1,15.2C88.7,88.7,87.9,74.3,78.4,74z M139.7,89.2\n\t\tc10.5-0.3,9.8-14.7,0.4-15.2C129.6,74.3,130.2,88.7,139.7,89.2z M98.4,99.2c2.2,3.5,5.3,3.3,9.8,0.2c3.8,3.2,8.1,3.3,9.2-0.2\n\t\tc-3.8,1.2-7,0.8-9.4-3.5C105.9,100.1,102.6,100.4,98.4,99.2z\"\n            tools:ignore=\"VectorPath\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "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=\"@color/color_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "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=\"@color/color_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"color_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"nf_logcat_status\" type=\"id\" />\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"BootstrapTheme\" parent=\"AppThemeLight\" />\n    <style name=\"SplashTheme\" parent=\"Theme.SplashScreen\">\n        <item name=\"windowSplashScreenAnimatedIcon\">@drawable/ic_launcher_foreground</item>\n        <item name=\"postSplashScreenTheme\">@style/AppThemeLight</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"BootstrapTheme\" parent=\"AppThemeDark\" />\n    <style name=\"SplashTheme\" parent=\"Theme.SplashScreen\">\n        <item name=\"windowSplashScreenAnimatedIcon\">@drawable/ic_launcher_foreground</item>\n        <item name=\"postSplashScreenTheme\">@style/AppThemeDark</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/xml/full_backup_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<full-backup-content>\n    <include\n        domain=\"sharedpref\"\n        path=\".\" />\n    <include\n        domain=\"database\"\n        path=\".\" />\n    <include\n        domain=\"file\"\n        path=\"imported\" />\n    <include\n        domain=\"file\"\n        path=\"pending\" />\n    <include\n        domain=\"file\"\n        path=\"clash/override.json\" />\n</full-backup-content>"
  },
  {
    "path": "app/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"AcceptsUserCertificates\">\n    <base-config>\n        <trust-anchors>\n            <certificates src=\"system\" />\n            <certificates src=\"user\" />\n        </trust-anchors>\n    </base-config>\n</network-security-config>"
  },
  {
    "path": "build.gradle.kts",
    "content": "@file:Suppress(\"UNUSED_VARIABLE\")\n\nimport com.android.build.gradle.AppExtension\nimport com.android.build.gradle.BaseExtension\nimport java.net.URL\nimport java.util.*\n\nbuildscript {\n    repositories {\n        mavenLocal()\n        mavenCentral()\n        gradlePluginPortal()\n        google()\n        maven(\"https://jitpack.io\")\n        maven(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n        maven(\"https://maven.kr328.app/releases\")\n    }\n    dependencies {\n        classpath(libs.build.android)\n        classpath(libs.build.kotlin.common)\n        classpath(libs.build.kotlin.serialization)\n        classpath(libs.build.ksp)\n        classpath(libs.build.golang)\n    }\n}\n\nsubprojects {\n    repositories {\n        mavenCentral()\n        google()\n        maven(\"https://maven.kr328.app/releases\")\n    }\n\n    val isApp = name == \"app\"\n\n    apply(plugin = if (isApp) \"com.android.application\" else \"com.android.library\")\n\n    extensions.configure<BaseExtension> {\n        defaultConfig {\n            if (isApp) {\n                applicationId = \"yos.clash.material\"\n            }\n\n            minSdk = 21\n            targetSdk = 34\n\n            versionName = \"0.1.2\"\n            versionCode = 3\n\n            resValue(\"string\", \"release_name\", \"v$versionName\")\n            resValue(\"integer\", \"release_code\", \"$versionCode\")\n\n            externalNativeBuild {\n                cmake {\n                    abiFilters(\"arm64-v8a\", \"armeabi-v7a\", \"x86\", \"x86_64\")\n                }\n            }\n\n            if (!isApp) {\n                consumerProguardFiles(\"consumer-rules.pro\")\n            } else {\n                setProperty(\"archivesBaseName\", \"clash-you-${versionName}-yosx\")\n            }\n        }\n\n        ndkVersion = \"23.0.7599858\"\n\n        compileSdkVersion(defaultConfig.targetSdk!!)\n\n        if (isApp) {\n            packagingOptions {\n                resources {\n                    excludes.add(\"DebugProbesKt.bin\")\n                }\n            }\n        }\n\n        productFlavors {\n            flavorDimensions(\"feature\")\n\n            /*create(\"foss\") {\n                isDefault = true\n                dimension = flavorDimensionList[0]\n                versionNameSuffix = \".foss\"\n\n                buildConfigField(\"boolean\", \"PREMIUM\", \"Boolean.parseBoolean(\\\"false\\\")\")\n\n                if (isApp) {\n                    applicationIdSuffix = \".foss\"\n                }\n            }*/\n\n            create(\"foss\") {\n                dimension = flavorDimensionList[0]\n                versionNameSuffix = \"\"\n\n                buildConfigField(\"boolean\", \"PREMIUM\", \"Boolean.parseBoolean(\\\"true\\\")\")\n            }\n            create(\"premium\") {\n                dimension = flavorDimensionList[0]\n                versionNameSuffix = \".premium\"\n\n                buildConfigField(\"boolean\", \"PREMIUM\", \"Boolean.parseBoolean(\\\"true\\\")\")\n            }\n        }\n\n        signingConfigs {\n            val keystore = rootProject.file(\"signing.properties\")\n            if (keystore.exists()) {\n                create(\"release\") {\n                    val prop = Properties().apply {\n                        keystore.inputStream().use(this::load)\n                    }\n\n                    storeFile = rootProject.file(prop.getProperty(\"keystore.path\")!!)\n                    storePassword = prop.getProperty(\"keystore.password\")!!\n                    keyAlias = prop.getProperty(\"key.alias\")!!\n                    keyPassword = prop.getProperty(\"key.password\")!!\n                }\n            }\n        }\n\n        buildTypes {\n            named(\"release\") {\n                isMinifyEnabled = isApp\n                isShrinkResources = isApp\n                signingConfig = signingConfigs.findByName(\"release\")\n                proguardFiles(\n                    getDefaultProguardFile(\"proguard-android-optimize.txt\"),\n                    \"proguard-rules.pro\"\n                )\n            }\n            named(\"debug\") {\n                versionNameSuffix = \".debug\"\n            }\n        }\n\n        buildFeatures.apply {\n            dataBinding {\n                isEnabled = name != \"hideapi\"\n            }\n        }\n\n        variantFilter {\n            ignore = name.startsWith(\"premium\") && !project(\":core\")\n                .file(\"src/premium/golang/clash/go.mod\").exists()\n        }\n\n        if (isApp) {\n            this as AppExtension\n\n            splits {\n                abi {\n                    isEnable = true\n                    isUniversalApk = true\n                }\n            }\n        }\n    }\n}\n\ntask(\"clean\", type = Delete::class) {\n    delete(rootProject.buildDir)\n}\n\ntasks.wrapper {\n    distributionType = Wrapper.DistributionType.ALL\n\n    doLast {\n        val sha256 = URL(\"$distributionUrl.sha256\").openStream()\n            .use { it.reader().readText().trim() }\n\n        file(\"gradle/wrapper/gradle-wrapper.properties\")\n            .appendText(\"distributionSha256Sum=$sha256\")\n    }\n}"
  },
  {
    "path": "common/build.gradle.kts",
    "content": "plugins {\n    kotlin(\"android\")\n    id(\"com.android.library\")\n}\n\ndependencies {\n    compileOnly(project(\":hideapi\"))\n\n    implementation(libs.kotlin.coroutine)\n    implementation(libs.androidx.core)\n}\n"
  },
  {
    "path": "common/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "common/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.\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"
  },
  {
    "path": "common/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"yos.clash.material.common\">\n\n    <permission\n        android:name=\"${applicationId}.permission.RECEIVE_BROADCASTS\"\n        android:description=\"@string/receive_broadcasts_of_clash\"\n        android:label=\"@string/receive_clash_broadcasts\"\n        android:protectionLevel=\"privileged|signature\" />\n    <uses-permission android:name=\"${applicationId}.permission.RECEIVE_BROADCASTS\" />\n</manifest>\n"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/Global.kt",
    "content": "package yos.clash.material.common\n\nimport android.app.Application\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.cancel\n\nobject Global : CoroutineScope by CoroutineScope(Dispatchers.IO) {\n    val application: Application\n        get() = application_\n\n    private lateinit var application_: Application\n\n    fun init(application: Application) {\n        this.application_ = application\n    }\n\n    fun destroy() {\n        cancel()\n    }\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/App.kt",
    "content": "package yos.clash.material.common.compat\n\nimport android.app.ActivityThread\nimport android.app.Application\nimport android.graphics.drawable.AdaptiveIconDrawable\nimport android.graphics.drawable.Drawable\nimport android.os.Build\nimport yos.clash.material.common.log.Log\n\nval Application.currentProcessName: String\n    get() {\n        if (Build.VERSION.SDK_INT >= 28)\n            return Application.getProcessName()\n\n        return try {\n            ActivityThread.currentProcessName()\n        } catch (throwable: Throwable) {\n            Log.w(\"Resolve process name: $throwable\")\n\n            packageName\n        }\n    }\n\nfun Drawable.foreground(): Drawable {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&\n        this is AdaptiveIconDrawable && this.background == null\n    ) {\n        return this.foreground\n    }\n    return this\n}\n"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/Context.kt",
    "content": "@file:Suppress(\"DEPRECATION\")\n\npackage yos.clash.material.common.compat\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport androidx.annotation.ColorRes\nimport androidx.annotation.DrawableRes\nimport androidx.core.content.ContextCompat\n\nfun Context.getColorCompat(@ColorRes id: Int): Int {\n    return ContextCompat.getColor(this, id)\n}\n\nfun Context.getDrawableCompat(@DrawableRes id: Int): Drawable? {\n    return ContextCompat.getDrawable(this, id)\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/Html.kt",
    "content": "@file:Suppress(\"DEPRECATION\")\n\npackage yos.clash.material.common.compat\n\nimport android.os.Build\nimport android.text.Html\nimport android.text.Spanned\n\nfun fromHtmlCompat(content: String): Spanned {\n    return if (Build.VERSION.SDK_INT >= 24) {\n        Html.fromHtml(content, Html.FROM_HTML_MODE_COMPACT)\n    } else {\n        Html.fromHtml(content)\n    }\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/Intents.kt",
    "content": "package yos.clash.material.common.compat\n\nimport android.app.PendingIntent\nimport android.os.Build\n\nfun pendingIntentFlags(flags: Int, mutable: Boolean = false): Int {\n    return if (Build.VERSION.SDK_INT >= 24) {\n        if (Build.VERSION.SDK_INT > 30 && mutable) {\n            flags or PendingIntent.FLAG_MUTABLE\n        } else {\n            flags or PendingIntent.FLAG_IMMUTABLE\n        }\n    } else {\n        flags\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/Package.kt",
    "content": "@file:Suppress(\"DEPRECATION\")\n\npackage yos.clash.material.common.compat\n\nimport android.content.pm.PackageInfo\n\nval PackageInfo.versionCodeCompat: Long\n    get() {\n        return if (android.os.Build.VERSION.SDK_INT >= 28) {\n            longVersionCode\n        } else {\n            versionCode.toLong()\n        }\n    }\n"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/Resource.kt",
    "content": "@file:Suppress(\"DEPRECATION\")\n\npackage yos.clash.material.common.compat\n\nimport android.content.res.Configuration\nimport android.os.Build\nimport java.util.*\n\nval Configuration.preferredLocale: Locale\n    get() {\n        return if (Build.VERSION.SDK_INT >= 24) {\n            locales[0]\n        } else {\n            locale\n        }\n    }"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/Services.kt",
    "content": "package yos.clash.material.common.compat\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\n\nfun Context.startForegroundServiceCompat(intent: Intent) {\n    if (Build.VERSION.SDK_INT >= 26) {\n        startForegroundService(intent)\n    } else {\n        startService(intent)\n    }\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/UI.kt",
    "content": "@file:Suppress(\"DEPRECATION\")\n\npackage yos.clash.material.common.compat\n\nimport android.annotation.TargetApi\nimport android.os.Build\nimport android.view.View\nimport android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR\nimport android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR\nimport android.view.Window\nimport android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS\nimport android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS\nimport android.view.WindowManager\n\nvar Window.isSystemBarsTranslucentCompat: Boolean\n    get() {\n        throw UnsupportedOperationException(\"set value only\")\n    }\n    set(value) {\n        if (Build.VERSION.SDK_INT >= 30) {\n            setDecorFitsSystemWindows(!value)\n        } else {\n            decorView.systemUiVisibility =\n                if (value) {\n                    decorView.systemUiVisibility or\n                            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or\n                            View.SYSTEM_UI_FLAG_LAYOUT_STABLE or\n                            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION\n                } else {\n                    decorView.systemUiVisibility and\n                            (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or\n                                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or\n                                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION).inv()\n                }\n        }\n\n        if (Build.VERSION.SDK_INT >= 28) {\n            if (value) {\n                attributes.layoutInDisplayCutoutMode =\n                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES\n            } else {\n                attributes.layoutInDisplayCutoutMode =\n                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT\n            }\n        }\n    }\n\nvar Window.isLightStatusBarsCompat: Boolean\n    get() {\n        throw UnsupportedOperationException(\"set value only\")\n    }\n    @TargetApi(23)\n    set(value) {\n        if (value) {\n            if (Build.VERSION.SDK_INT >= 30) {\n                decorView.windowInsetsController?.apply {\n                    setSystemBarsAppearance(\n                        APPEARANCE_LIGHT_STATUS_BARS,\n                        APPEARANCE_LIGHT_STATUS_BARS\n                    )\n                }\n            } else {\n                decorView.systemUiVisibility =\n                    decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR\n            }\n        } else {\n            if (Build.VERSION.SDK_INT >= 30) {\n                decorView.windowInsetsController?.apply {\n                    setSystemBarsAppearance(\n                        0,\n                        APPEARANCE_LIGHT_STATUS_BARS\n                    )\n                }\n            } else {\n                decorView.systemUiVisibility =\n                    decorView.systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()\n            }\n        }\n    }\n\nvar Window.isLightNavigationBarCompat: Boolean\n    get() {\n        throw UnsupportedOperationException(\"set value only\")\n    }\n    @TargetApi(27)\n    set(value) {\n        if (value) {\n            if (Build.VERSION.SDK_INT >= 30) {\n                decorView.windowInsetsController?.apply {\n                    setSystemBarsAppearance(\n                        APPEARANCE_LIGHT_NAVIGATION_BARS,\n                        APPEARANCE_LIGHT_NAVIGATION_BARS\n                    )\n                }\n            } else {\n                decorView.systemUiVisibility =\n                    decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR\n            }\n        } else {\n            if (Build.VERSION.SDK_INT >= 30) {\n                decorView.windowInsetsController?.apply {\n                    setSystemBarsAppearance(\n                        0,\n                        APPEARANCE_LIGHT_NAVIGATION_BARS\n                    )\n                }\n            } else {\n                decorView.systemUiVisibility =\n                    decorView.systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()\n            }\n        }\n    }\n\nvar Window.isAllowForceDarkCompat: Boolean\n    get() {\n        return if (Build.VERSION.SDK_INT >= 29) {\n            decorView.isForceDarkAllowed\n        } else {\n            false\n        }\n    }\n    set(value) {\n        if (Build.VERSION.SDK_INT >= 29) {\n            decorView.isForceDarkAllowed = value\n        }\n    }"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/compat/View.kt",
    "content": "@file:Suppress(\"DEPRECATION\")\n\npackage yos.clash.material.common.compat\n\nimport android.os.Build\nimport android.widget.TextView\nimport androidx.annotation.StyleRes\n\nvar TextView.textAppearance: Int\n    get() = throw UnsupportedOperationException(\"set value only\")\n    set(@StyleRes value) {\n        if (Build.VERSION.SDK_INT >= 23) {\n            setTextAppearance(value)\n        } else {\n            setTextAppearance(context, value)\n        }\n    }"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/constants/Authorities.kt",
    "content": "package yos.clash.material.common.constants\n\nimport yos.clash.material.common.util.packageName\n\nobject Authorities {\n    val STATUS_PROVIDER = \"$packageName.status\"\n    val SETTINGS_PROVIDER = \"$packageName.settings\"\n    val FILES_PROVIDER = \"$packageName.files\"\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/constants/Components.kt",
    "content": "package yos.clash.material.common.constants\n\nimport android.content.ComponentName\nimport yos.clash.material.common.util.packageName\n\nobject Components {\n    private const val componentsPackageName = \"yos.clash.material\"\n\n    val MAIN_ACTIVITY = ComponentName(packageName, \"$componentsPackageName.MainActivity\")\n    val PROPERTIES_ACTIVITY = ComponentName(packageName, \"$componentsPackageName.PropertiesActivity\")\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/constants/Intents.kt",
    "content": "package yos.clash.material.common.constants\n\nimport yos.clash.material.common.util.packageName\n\nobject Intents {\n    // Public\n    val ACTION_PROVIDE_URL = \"$packageName.action.PROVIDE_URL\"\n\n    const val EXTRA_NAME = \"name\"\n\n    // Self\n    val ACTION_SERVICE_RECREATED = \"$packageName.intent.action.CLASH_RECREATED\"\n    val ACTION_CLASH_STARTED = \"$packageName.intent.action.CLASH_STARTED\"\n    val ACTION_CLASH_STOPPED = \"$packageName.intent.action.CLASH_STOPPED\"\n    val ACTION_CLASH_REQUEST_STOP = \"$packageName.intent.action.CLASH_REQUEST_STOP\"\n    val ACTION_PROFILE_CHANGED = \"$packageName.intent.action.PROFILE_CHANGED\"\n    val ACTION_PROFILE_REQUEST_UPDATE = \"$packageName.intent.action.REQUEST_UPDATE\"\n    val ACTION_PROFILE_SCHEDULE_UPDATES = \"$packageName.intent.action.SCHEDULE_UPDATES\"\n    val ACTION_PROFILE_LOADED = \"$packageName.intent.action.PROFILE_LOADED\"\n    val ACTION_OVERRIDE_CHANGED = \"$packageName.intent.action.OVERRIDE_CHANGED\"\n\n    const val EXTRA_STOP_REASON = \"stop_reason\"\n    const val EXTRA_UUID = \"uuid\"\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/constants/Metadata.kt",
    "content": "package yos.clash.material.common.constants\n\nimport yos.clash.material.common.util.packageName\n\nobject Metadata {\n    val GEOIP_FILE_NAME = \"$packageName.GEOIP_FILE_NAME\"\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/constants/Permissions.kt",
    "content": "package yos.clash.material.common.constants\n\nimport yos.clash.material.common.util.packageName\n\nobject Permissions {\n    val RECEIVE_SELF_BROADCASTS = \"$packageName.permission.RECEIVE_BROADCASTS\"\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/id/UndefinedIds.kt",
    "content": "package yos.clash.material.common.id\n\nobject UndefinedIds {\n    private const val PREFIX = 0x14000000\n    private const val MASK = 0x00FFFFFF\n\n    private var current: Int = 0\n\n    @Synchronized\n    fun next(): Int {\n        current = ((current and MASK) + 1 or PREFIX)\n\n        return current\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/log/Log.kt",
    "content": "package yos.clash.material.common.log\n\nobject Log {\n    private const val TAG = \"ClashForAndroid\"\n\n    fun i(message: String, throwable: Throwable? = null) =\n        android.util.Log.i(TAG, message, throwable)\n\n    fun w(message: String, throwable: Throwable? = null) =\n        android.util.Log.w(TAG, message, throwable)\n\n    fun e(message: String, throwable: Throwable? = null) =\n        android.util.Log.e(TAG, message, throwable)\n\n    fun d(message: String, throwable: Throwable? = null) =\n        android.util.Log.d(TAG, message, throwable)\n\n    fun v(message: String, throwable: Throwable? = null) =\n        android.util.Log.v(TAG, message, throwable)\n\n    fun f(message: String, throwable: Throwable) =\n        android.util.Log.wtf(message, throwable)\n}\n"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/store/Providers.kt",
    "content": "package yos.clash.material.common.store\n\nimport android.content.SharedPreferences\nimport androidx.core.content.edit\n\nclass SharedPreferenceProvider(private val preferences: SharedPreferences) : StoreProvider {\n    override fun getInt(key: String, defaultValue: Int): Int {\n        return preferences.getInt(key, defaultValue)\n    }\n\n    override fun setInt(key: String, value: Int) {\n        preferences.edit {\n            putInt(key, value)\n        }\n    }\n\n    override fun getLong(key: String, defaultValue: Long): Long {\n        return preferences.getLong(key, defaultValue)\n    }\n\n    override fun setLong(key: String, value: Long) {\n        preferences.edit {\n            putLong(key, value)\n        }\n    }\n\n    override fun getString(key: String, defaultValue: String): String {\n        return preferences.getString(key, defaultValue)!!\n    }\n\n    override fun setString(key: String, value: String) {\n        preferences.edit {\n            putString(key, value)\n        }\n    }\n\n    override fun getStringSet(key: String, defaultValue: Set<String>): Set<String> {\n        return preferences.getStringSet(key, defaultValue)!!\n    }\n\n    override fun setStringSet(key: String, value: Set<String>) {\n        preferences.edit {\n            putStringSet(key, value)\n        }\n    }\n\n    override fun getBoolean(key: String, defaultValue: Boolean): Boolean {\n        return preferences.getBoolean(key, defaultValue)\n    }\n\n    override fun setBoolean(key: String, value: Boolean) {\n        preferences.edit {\n            putBoolean(key, value)\n        }\n    }\n}\n\nfun SharedPreferences.asStoreProvider(): StoreProvider {\n    return SharedPreferenceProvider(this)\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/store/Store.kt",
    "content": "package yos.clash.material.common.store\n\nimport kotlin.reflect.KProperty\n\nclass Store(val provider: StoreProvider) {\n    interface Delegate<T> {\n        operator fun getValue(thisRef: Any?, property: KProperty<*>): T\n        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)\n    }\n\n    fun int(key: String, defaultValue: Int): Delegate<Int> {\n        return object : Delegate<Int> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): Int {\n                return provider.getInt(key, defaultValue)\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {\n                provider.setInt(key, value)\n            }\n        }\n    }\n\n    fun long(key: String, defaultValue: Long): Delegate<Long> {\n        return object : Delegate<Long> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): Long {\n                return provider.getLong(key, defaultValue)\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) {\n                provider.setLong(key, value)\n            }\n        }\n    }\n\n    fun string(key: String, defaultValue: String): Delegate<String> {\n        return object : Delegate<String> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): String {\n                return provider.getString(key, defaultValue)\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {\n                provider.setString(key, value)\n            }\n        }\n    }\n\n    fun stringSet(key: String, defaultValue: Set<String>): Delegate<Set<String>> {\n        return object : Delegate<Set<String>> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): Set<String> {\n                return provider.getStringSet(key, defaultValue)\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: Set<String>) {\n                provider.setStringSet(key, value)\n            }\n        }\n    }\n\n    fun boolean(key: String, defaultValue: Boolean): Delegate<Boolean> {\n        return object : Delegate<Boolean> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {\n                return provider.getBoolean(key, defaultValue)\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {\n                provider.setBoolean(key, value)\n            }\n        }\n    }\n\n    fun <T : Enum<T>> enum(key: String, defaultValue: T, values: Array<T>): Delegate<T> {\n        return object : Delegate<T> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): T {\n                val name = provider.getString(key, defaultValue.name)\n\n                return values.find { name == it.name } ?: defaultValue\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {\n                provider.setString(key, value.name)\n            }\n        }\n    }\n\n    fun <T> typedString(key: String, from: (String) -> T?, to: (T?) -> String): Delegate<T?> {\n        return object : Delegate<T?> {\n            override fun getValue(thisRef: Any?, property: KProperty<*>): T? {\n                val value = provider.getString(key, to(null))\n\n                return from(value)\n            }\n\n            override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {\n                provider.setString(key, to(value))\n            }\n        }\n    }\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/store/StoreProvider.kt",
    "content": "package yos.clash.material.common.store\n\ninterface StoreProvider {\n    fun getInt(key: String, defaultValue: Int): Int\n    fun setInt(key: String, value: Int)\n\n    fun getLong(key: String, defaultValue: Long): Long\n    fun setLong(key: String, value: Long)\n\n    fun getString(key: String, defaultValue: String): String\n    fun setString(key: String, value: String)\n\n    fun getStringSet(key: String, defaultValue: Set<String>): Set<String>\n    fun setStringSet(key: String, value: Set<String>)\n\n    fun getBoolean(key: String, defaultValue: Boolean): Boolean\n    fun setBoolean(key: String, value: Boolean)\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/util/Components.kt",
    "content": "package yos.clash.material.common.util\n\nimport android.content.ComponentName\nimport android.content.Intent\nimport yos.clash.material.common.Global\nimport kotlin.reflect.KClass\n\nval KClass<*>.componentName: ComponentName\n    get() = ComponentName(Global.application.packageName, this.java.name)\n\nval KClass<*>.intent: Intent\n    get() = Intent(Global.application, this.java)"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/util/Global.kt",
    "content": "package yos.clash.material.common.util\n\nimport yos.clash.material.common.Global\n\nval packageName: String = Global.application.packageName"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/util/Intent.kt",
    "content": "package yos.clash.material.common.util\n\nimport android.content.Intent\nimport android.net.Uri\nimport java.util.*\n\nfun Intent.grantPermissions(read: Boolean = true, write: Boolean = true): Intent {\n    var flags = 0\n\n    if (read)\n        flags = flags or Intent.FLAG_GRANT_READ_URI_PERMISSION\n\n    if (write)\n        flags = flags or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n\n    addFlags(flags)\n\n    return this\n}\n\nvar Intent.fileName: String?\n    get() {\n        return data?.takeIf { it.scheme == \"file\" }?.schemeSpecificPart\n    }\n    set(value) {\n        data = Uri.fromParts(\"file\", value, null)\n    }\n\nvar Intent.uuid: UUID?\n    get() {\n        return data?.takeIf { it.scheme == \"uuid\" }?.schemeSpecificPart?.let(UUID::fromString)\n    }\n    set(value) {\n        data = Uri.fromParts(\"uuid\", value.toString(), null)\n    }\n\nfun Intent.setUUID(uuid: UUID): Intent {\n    this.uuid = uuid\n\n    return this\n}\n\nfun Intent.setFileName(fileName: String): Intent {\n    this.fileName = fileName\n\n    return this\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/util/Parcelable.kt",
    "content": "package yos.clash.material.common.util\n\nimport android.os.Binder\nimport android.os.Parcel\nimport android.os.Parcelable\n\nprivate class SliceParcelableListBpBinder(val list: List<Parcelable>, val flags: Int) : Binder() {\n    override fun onTransact(code: Int, data: Parcel, reply: Parcel?, tFlags: Int): Boolean {\n        when (code) {\n            TRANSACTION_GET_ITEMS -> {\n                reply ?: return false\n\n                val offset = data.readInt()\n                val chunk = data.readInt()\n\n                val end = (offset + chunk).coerceAtMost(list.size)\n\n                reply.writeInt(end - offset)\n\n                for (i in offset until end) {\n                    list[i].writeToParcel(reply, flags)\n                }\n\n                return true\n            }\n        }\n\n        return super.onTransact(code, data, reply, flags)\n    }\n\n    companion object {\n        const val TRANSACTION_GET_ITEMS = 10\n    }\n}\n\nfun <T : Parcelable> List<T>.writeToParcelSlice(parcel: Parcel, flags: Int) {\n    val bp = SliceParcelableListBpBinder(this, flags)\n\n    parcel.writeInt(size)\n    parcel.writeStrongBinder(bp)\n}\n\nfun <T : Parcelable> Parcelable.Creator<T>.createListFromParcelSlice(\n    parcel: Parcel,\n    flags: Int,\n    chunk: Int,\n): List<T> {\n    val total = parcel.readInt()\n    val remote = parcel.readStrongBinder()\n    val result = ArrayList<T>(total)\n\n    var offset = 0\n\n    while (offset < total) {\n        val data = Parcel.obtain()\n        val reply = Parcel.obtain()\n\n        try {\n            data.writeInt(offset)\n            data.writeInt(chunk)\n\n            if (!remote.transact(\n                    SliceParcelableListBpBinder.TRANSACTION_GET_ITEMS,\n                    data,\n                    reply,\n                    flags\n                )\n            ) {\n                break\n            }\n\n            val size = reply.readInt()\n\n            repeat(size) {\n                result.add(createFromParcel(reply))\n            }\n\n            offset += size\n\n            if (size == 0)\n                break\n        } finally {\n            data.recycle()\n            reply.recycle()\n        }\n    }\n\n    return result\n}"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/util/Patterns.kt",
    "content": "package yos.clash.material.common.util\n\nval PatternFileName = Regex(\"[^*&%\\\\n\\\\r/]+\")"
  },
  {
    "path": "common/src/main/java/yos/clash/material/common/util/Ticker.kt",
    "content": "package yos.clash.material.common.util\n\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\n\nfun CoroutineScope.ticker(period: Long): Channel<Long> {\n    val channel = Channel<Long>(Channel.RENDEZVOUS)\n\n    launch {\n        try {\n            while (isActive) {\n                channel.send(System.currentTimeMillis())\n\n                delay(period)\n            }\n        } catch (ignored: Exception) {\n\n        }\n    }\n\n    return channel\n}"
  },
  {
    "path": "common/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"receive_clash_broadcasts\">Receive Clash You Broadcasts</string>\n    <string name=\"receive_broadcasts_of_clash\">Receive broadcasts of Clash You services</string>\n</resources>\n"
  },
  {
    "path": "common/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"receive_clash_broadcasts\">接收 Clash You 广播</string>\n    <string name=\"receive_broadcasts_of_clash\">接收来自 Clash You 内部的广播</string>\n</resources>"
  },
  {
    "path": "common/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"receive_clash_broadcasts\">接收 Clash You 廣播</string>\n    <string name=\"receive_broadcasts_of_clash\">接收來自 Clash You 內部的廣播</string>\n</resources>\n"
  },
  {
    "path": "core/build.gradle.kts",
    "content": "import com.github.kr328.golang.GolangBuildTask\nimport com.github.kr328.golang.GolangPlugin\nimport java.io.FileOutputStream\nimport java.net.URL\nimport java.time.Duration\n\nplugins {\n    kotlin(\"android\")\n    id(\"com.android.library\")\n    id(\"kotlinx-serialization\")\n    id(\"golang-android\")\n}\n\nval geoipDatabaseUrl =\n    \"https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb\"\nval geoipInvalidate = Duration.ofDays(7)!!\nval geoipOutput = buildDir.resolve(\"intermediates/golang_blob\")\nval golangSource = file(\"src/main/golang/native\")\n\ngolang {\n    sourceSets {\n        create(\"foss\") {\n            tags.set(listOf(\"foss\"))\n            srcDir.set(file(\"src/foss/golang\"))\n        }\n        create(\"premium\") {\n            tags.set(listOf(\"premium\", \"without_gvisor\", \"without_system\"))\n            srcDir.set(file(\"src/premium/golang\"))\n        }\n        all {\n            fileName.set(\"libclash.so\")\n            packageName.set(\"cfa/native\")\n        }\n    }\n}\n\nandroid {\n    productFlavors {\n        all {\n            externalNativeBuild {\n                cmake {\n                    arguments(\"-DGO_SOURCE:STRING=${golangSource}\")\n                    arguments(\"-DGO_OUTPUT:STRING=${GolangPlugin.outputDirOf(project, null, null)}\")\n                    arguments(\"-DFLAVOR_NAME:STRING=$name\")\n                }\n            }\n        }\n    }\n\n    externalNativeBuild {\n        cmake {\n            path = file(\"src/main/cpp/CMakeLists.txt\")\n        }\n    }\n}\n\ndependencies {\n    implementation(project(\":common\"))\n\n    implementation(libs.androidx.core)\n    implementation(libs.kotlin.coroutine)\n    implementation(libs.kotlin.serialization.json)\n}\n\nafterEvaluate {\n    tasks.withType(GolangBuildTask::class.java).forEach {\n        it.inputs.dir(golangSource)\n    }\n}\n\ntask(\"downloadGeoipDatabase\") {\n    val databaseFile = geoipOutput.resolve(\"Country.mmdb\")\n    val moduleFile = geoipOutput.resolve(\"go.mod\")\n    val sourceFile = geoipOutput.resolve(\"blob.go\")\n\n    val moduleContent = \"\"\"\n        module \"cfa/blob\"\n    \"\"\".trimIndent()\n\n    val sourceContent = \"\"\"\n        package blob\n        \n        import _ \"embed\"\n        \n        //go:embed Country.mmdb\n        var GeoipDatabase []byte\n    \"\"\".trimIndent()\n\n    outputs.dir(geoipOutput)\n\n    onlyIf {\n        System.currentTimeMillis() - databaseFile.lastModified() > geoipInvalidate.toMillis()\n    }\n\n    doLast {\n        geoipOutput.mkdirs()\n\n        moduleFile.writeText(moduleContent)\n        sourceFile.writeText(sourceContent)\n\n        URL(geoipDatabaseUrl).openConnection().getInputStream().use { input ->\n            FileOutputStream(databaseFile).use { output ->\n                input.copyTo(output)\n            }\n        }\n    }\n}\n\nafterEvaluate {\n    val downloadTask = tasks[\"downloadGeoipDatabase\"]\n\n    tasks.forEach {\n        if (it.name.startsWith(\"externalGolangBuild\")) {\n            it.dependsOn(downloadTask)\n        }\n    }\n}"
  },
  {
    "path": "core/consumer-rules.pro",
    "content": "-keep class kotlinx.coroutines.CompletableDeferred {\n    *;\n}\n\n-keep class kotlin.Unit {\n    *;\n}\n\n-keepattributes *Annotation*, InnerClasses\n-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations\n\n# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer\n-keepclassmembers class kotlinx.serialization.json.** {\n    *** Companion;\n}\n-keepclasseswithmembers class kotlinx.serialization.json.** {\n    kotlinx.serialization.KSerializer serializer(...);\n}\n"
  },
  {
    "path": "core/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.\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"
  },
  {
    "path": "core/src/foss/golang/go.mod",
    "content": "module foss\n\ngo 1.18\n\nrequire cfa v0.0.0\n\nrequire (\n\tcfa/blob v0.0.0 // indirect\n\tgithub.com/Dreamacro/clash v1.7.1 // indirect\n\tgithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34 // indirect\n\tgithub.com/dlclark/regexp2 v1.4.0 // indirect\n\tgithub.com/gofrs/uuid v4.2.0+incompatible // indirect\n\tgithub.com/gorilla/websocket v1.5.0 // indirect\n\tgithub.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect\n\tgithub.com/kr/pretty v0.1.0 // indirect\n\tgithub.com/miekg/dns v1.1.49 // indirect\n\tgithub.com/oschwald/geoip2-golang v1.7.0 // indirect\n\tgithub.com/oschwald/maxminddb-golang v1.9.0 // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect\n\tgo.etcd.io/bbolt v1.3.6 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgolang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect\n\tgolang.org/x/mod v0.4.2 // indirect\n\tgolang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect\n\tgolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect\n\tgolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgolang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect\n\tgolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect\n\tgopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace cfa => ../../main/golang\n\nreplace github.com/Dreamacro/clash => ./clash\n\nreplace cfa/blob => ../../../build/intermediates/golang_blob\n"
  },
  {
    "path": "core/src/foss/golang/go.sum",
    "content": "github.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34 h1:USCTqih5d1bUXUxWNS9ZD5Tx/lb0jXHEtRIIx/F9dMc=\ngithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34/go.mod h1:YR9wK13TgI5ww8iKWm91MHiSoHC7Oz0U4beCCmtXqLw=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=\ngithub.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=\ngithub.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=\ngithub.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=\ngithub.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=\ngithub.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=\ngithub.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo=\ngithub.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=\ngithub.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=\ngithub.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=\ngithub.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=\ngithub.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=\ngithub.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=\ngithub.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=\ngithub.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=\ngithub.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=\ngithub.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8=\ngithub.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ=\ngithub.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=\ngithub.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=\ngithub.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=\ngithub.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 h1:XMAtQHwKjWHIRwg+8Nj/rzUomQY1q6cM3ncA0wP8GU4=\ngithub.com/u-root/uio v0.0.0-20210528151154-e40b768296a7/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=\ngo.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=\ngolang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=\ngolang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=\ngolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=\ngolang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "core/src/foss/golang/main.go",
    "content": "package golang\n\nimport (\n\t_ \"cfa/native/all\"\n)\n"
  },
  {
    "path": "core/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"yos.clash.material.core\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n</manifest>\n"
  },
  {
    "path": "core/src/main/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.0)\n\nproject(clash-bridge C)\n\nset(CMAKE_POSITION_INDEPENDENT_CODE on)\nset(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE} -O3\")\n\nset(GO_OUTPUT_BASE ${GO_OUTPUT}/${FLAVOR_NAME})\n\nif (\"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n    set(GO_OUTPUT_BASE \"${GO_OUTPUT_BASE}Debug\")\nelseif (\"${CMAKE_BUILD_TYPE}\" STREQUAL \"Release\")\n    set(GO_OUTPUT_BASE \"${GO_OUTPUT_BASE}Release\")\nelseif (\"${CMAKE_BUILD_TYPE}\" STREQUAL \"RelWithDebInfo\")\n    set(GO_OUTPUT_BASE \"${GO_OUTPUT_BASE}Release\")\nelse ()\n    message(FATAL_ERROR \"Unknown build type ${CMAKE_BUILD_TYPE}\")\nendif ()\n\ninclude_directories(\"${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}\")\ninclude_directories(\"${GO_SOURCE}\")\n\nlink_directories(\"${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}\")\n\nadd_library(bridge SHARED main.c jni_helper.c bridge_helper.c)\ntarget_link_libraries(bridge log clash)"
  },
  {
    "path": "core/src/main/cpp/bridge_helper.c",
    "content": "#include \"bridge_helper.h\"\n\nuint64_t down_scale_traffic(uint64_t value) {\n    if (value > 1042 * 1024 * 1024)\n        return ((value * 100u / 1024u / 1024u / 1024u) & 0x3FFFFFFFu) | (3u << 30u);\n    if (value > 1024 * 1024)\n        return ((value * 100u / 1024u / 1024u) & 0x3FFFFFFFu) | (2u << 30u);\n    if (value > 1024)\n        return ((value * 100u / 1024u) & 0x3FFFFFFFu) | (1u << 30u);\n    return value & 0x3FFFFFFFu;\n}\n\n"
  },
  {
    "path": "core/src/main/cpp/bridge_helper.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\nuint64_t down_scale_traffic(uint64_t value);"
  },
  {
    "path": "core/src/main/cpp/jni_helper.c",
    "content": "#include \"jni_helper.h\"\n\n#include <malloc.h>\n#include <string.h>\n\nstatic JavaVM *global_vm;\n\nstatic jclass c_string;\nstatic jmethodID m_new_string;\nstatic jmethodID m_get_bytes;\n\nvoid initialize_jni(JavaVM *vm, JNIEnv *env) {\n    global_vm = vm;\n\n    c_string = (jclass) new_global(find_class(\"java/lang/String\"));\n    m_new_string = find_method(c_string, \"<init>\", \"([B)V\");\n    m_get_bytes = find_method(c_string, \"getBytes\", \"()[B\");\n}\n\nJavaVM *global_java_vm() {\n    return global_vm;\n}\n\nchar *jni_get_string(JNIEnv *env, jstring str) {\n    jbyteArray array = (*env)->CallObjectMethod(env, str, m_get_bytes);\n    int length = (*env)->GetArrayLength(env, array);\n\n    char *content = (char *) malloc(length + 1);\n\n    (*env)->GetByteArrayRegion(env, array, 0, length, (jbyte *) content);\n\n    content[length] = 0;\n\n    return content;\n}\n\njstring jni_new_string(JNIEnv *env, const char *str) {\n    int length = strlen(str);\n    jbyteArray array = (*env)->NewByteArray(env, length);\n\n    (*env)->SetByteArrayRegion(env, array, 0, length, (const jbyte *) str);\n\n    return (jstring) (*env)->NewObject(env, c_string, m_new_string, array);\n}\n\nint jni_catch_exception(JNIEnv *env) {\n    int result = (*env)->ExceptionCheck(env);\n\n    if (result) {\n        (*env)->ExceptionDescribe(env);\n        (*env)->ExceptionClear(env);\n    }\n\n    return result;\n}\n\nvoid jni_attach_thread(struct _scoped_jni *jni) {\n    JavaVM *vm = global_java_vm();\n\n    if ((*vm)->GetEnv(vm, (void **) &jni->env, JNI_VERSION_1_6) == JNI_OK) {\n        jni->require_release = 0;\n        return;\n    }\n\n    if ((*vm)->AttachCurrentThread(vm, &jni->env, NULL) != JNI_OK) {\n        abort();\n    }\n\n    jni->require_release = 1;\n}\n\nvoid jni_detach_thread(struct _scoped_jni *jni) {\n    JavaVM *vm = global_java_vm();\n\n    if (jni->require_release) {\n        (*vm)->DetachCurrentThread(vm);\n    }\n}\n\nvoid release_string(char **str) {\n    free(*str);\n}"
  },
  {
    "path": "core/src/main/cpp/jni_helper.h",
    "content": "#pragma once\n\n#include <jni.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <malloc.h>\n#include <android/log.h>\n\nstruct _scoped_jni {\n    JNIEnv *env;\n    int require_release;\n};\n\nextern void initialize_jni(JavaVM *vm, JNIEnv *env);\nextern jstring jni_new_string(JNIEnv *env, const char *str);\nextern char *jni_get_string(JNIEnv *env, jstring str);\nextern int jni_catch_exception(JNIEnv *env);\nextern void jni_attach_thread(struct _scoped_jni *jni);\nextern void jni_detach_thread(struct _scoped_jni *env);\nextern void release_string(char **str);\n\n#define ATTACH_JNI() __attribute__((unused, cleanup(jni_detach_thread))) \\\n                    struct _scoped_jni _jni; \\\n                    jni_attach_thread(&_jni); \\\n                    JNIEnv *env = _jni.env\n\n#define scoped_string __attribute__((cleanup(release_string))) char*\n\n#define find_class(name) (*env)->FindClass(env, name)\n#define find_method(cls, name, signature) (*env)->GetMethodID(env, cls, name, signature)\n#define new_global(obj) (*env)->NewGlobalRef(env, obj)\n#define del_global(obj) (*env)->DeleteGlobalRef(env, obj)\n#define get_string(jstr) jni_get_string(env, jstr)\n#define new_string(cstr) jni_new_string(env, cstr)"
  },
  {
    "path": "core/src/main/cpp/main.c",
    "content": "#include <jni.h>\n#include <stdint.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"bridge_helper.h\"\n#include \"libclash.h\"\n#include \"jni_helper.h\"\n#include \"trace.h\"\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeInit(JNIEnv *env, jobject thiz,\n                                                          jstring home,\n                                                          jstring version_name, jint sdk_version) {\n    TRACE_METHOD();\n\n    scoped_string _home = get_string(home);\n    scoped_string _version_name = get_string(version_name);\n\n    coreInit(_home, _version_name, sdk_version);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeReset(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    reset();\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeForceGc(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    forceGc();\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeSuspend(JNIEnv *env, jobject thiz,\n                                                             jboolean suspended) {\n    TRACE_METHOD();\n\n    suspend((int) suspended);\n}\n\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryTunnelState(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    scoped_string response = queryTunnelState();\n\n    return new_string(response);\n}\n\nJNIEXPORT jlong JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryTrafficNow(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    uint64_t upload = 0l, download = 0l;\n\n    queryNow(&upload, &download);\n\n    return (jlong) (down_scale_traffic(upload) << 32u | down_scale_traffic(download));\n}\n\nJNIEXPORT jlong JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryTrafficTotal(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    uint64_t upload = 0l, download = 0l;\n\n    queryTotal(&upload, &download);\n\n    return (jlong) (down_scale_traffic(upload) << 32u | down_scale_traffic(download));\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyDnsChanged(JNIEnv *env, jobject thiz,\n                                                                      jstring dns_list) {\n    TRACE_METHOD();\n\n    scoped_string _dns_list = get_string(dns_list);\n\n    notifyDnsChanged(_dns_list);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyTimeZoneChanged(JNIEnv *env, jobject thiz,\n                                                                           jstring name, jint offset) {\n    TRACE_METHOD();\n\n    scoped_string _name = get_string(name);\n\n    notifyTimeZoneChanged(_name, offset);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyInstalledAppChanged(JNIEnv *env,\n                                                                               jobject thiz,\n                                                                               jstring uid_list) {\n    TRACE_METHOD();\n\n    scoped_string _uid_list = get_string(uid_list);\n\n    notifyInstalledAppsChanged(_uid_list);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz,\n                                                              jint fd,\n                                                              jstring gateway,\n                                                              jstring portal,\n                                                              jstring dns,\n                                                              jobject cb) {\n    TRACE_METHOD();\n\n    scoped_string _gateway = get_string(gateway);\n    scoped_string _portal = get_string(portal);\n    scoped_string _dns = get_string(dns);\n    jobject _interface = new_global(cb);\n\n    startTun(fd, _gateway, _portal, _dns, _interface);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeStopTun(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    stopTun();\n}\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeStartHttp(JNIEnv *env, jobject thiz,\n                                                               jstring listen_at) {\n    TRACE_METHOD();\n\n    scoped_string _listen_at = get_string(listen_at);\n\n    scoped_string listened = startHttp(_listen_at);\n\n    if (listened == NULL)\n        return NULL;\n\n    return new_string(listened);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeStopHttp(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    stopHttp();\n}\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryGroupNames(JNIEnv *env, jobject thiz,\n                                                                     jboolean exclude_not_selectable) {\n    TRACE_METHOD();\n\n    scoped_string response = queryGroupNames((int) exclude_not_selectable);\n\n    return new_string(response);\n}\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryGroup(JNIEnv *env, jobject thiz,\n                                                                jstring name, jstring mode) {\n    TRACE_METHOD();\n\n    scoped_string _name = get_string(name);\n    scoped_string _mode = get_string(mode);\n\n    scoped_string response = queryGroup(_name, _mode);\n\n    if (response == NULL)\n        return NULL;\n\n    return new_string(response);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeHealthCheck(JNIEnv *env, jobject thiz,\n                                                                 jobject completable,\n                                                                 jstring name) {\n    TRACE_METHOD();\n\n    jobject _completable = new_global(completable);\n    scoped_string _name = get_string(name);\n\n    healthCheck(_completable, _name);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeHealthCheckAll(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    healthCheckAll();\n}\n\nJNIEXPORT jboolean JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativePatchSelector(JNIEnv *env, jobject thiz,\n                                                                   jstring selector, jstring name) {\n    TRACE_METHOD();\n\n    scoped_string _selector = get_string(selector);\n    scoped_string _name = get_string(name);\n\n    return (jboolean) patchSelector(_selector, _name);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeLoad(JNIEnv *env, jobject thiz,\n                                                          jobject completable, jstring path) {\n    TRACE_METHOD();\n\n    jobject _completable = new_global(completable);\n    scoped_string _path = get_string(path);\n\n    load(_completable, _path);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeFetchAndValid(JNIEnv *env, jobject thiz,\n                                                                   jobject callback,\n                                                                   jstring path,\n                                                                   jstring url, jboolean force) {\n    TRACE_METHOD();\n\n    jobject _completable = new_global(callback);\n    scoped_string _path = get_string(path);\n    scoped_string _url = get_string(url);\n\n    fetchAndValid(_completable, _path, _url, force);\n}\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryProviders(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    scoped_string response = queryProviders();\n\n    return new_string(response);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeUpdateProvider(JNIEnv *env, jobject thiz,\n                                                                    jobject completable,\n                                                                    jstring type,\n                                                                    jstring name) {\n    TRACE_METHOD();\n\n    jobject _completable = new_global(completable);\n    scoped_string _type = get_string(type);\n    scoped_string _name = get_string(name);\n\n    updateProvider(_completable, _type, _name);\n}\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeReadOverride(JNIEnv *env, jobject thiz,\n                                                                  jint slot) {\n    TRACE_METHOD();\n\n    scoped_string response = readOverride(slot);\n\n    return new_string(response);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeWriteOverride(JNIEnv *env, jobject thiz,\n                                                                   jint slot,\n                                                                   jstring content) {\n    TRACE_METHOD();\n\n    scoped_string _content = get_string(content);\n\n    writeOverride(slot, _content);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeClearOverride(JNIEnv *env, jobject thiz,\n                                                                   jint slot) {\n    TRACE_METHOD();\n\n    clearOverride(slot);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeInstallSideloadGeoip(JNIEnv *env, jobject thiz,\n                                                                          jbyteArray data) {\n    TRACE_METHOD();\n\n    if (data == NULL) {\n        installSideloadGeoip(NULL, 0);\n\n        return;\n    }\n\n    jbyte *bytes = (*env)->GetByteArrayElements(env, data, NULL);\n    int size = (*env)->GetArrayLength(env, data);\n\n    scoped_string err = installSideloadGeoip(bytes, size);\n\n    (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);\n\n    if (err != NULL) {\n        (*env)->ThrowNew(\n                env,\n                find_class(\"com/github/kr328/clash/core/bridge/ClashException\"),\n                err\n        );\n    }\n}\n\nJNIEXPORT jstring JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeQueryConfiguration(JNIEnv *env, jobject thiz) {\n    TRACE_METHOD();\n\n    scoped_string response = queryConfiguration();\n\n    return new_string(response);\n}\n\nJNIEXPORT void JNICALL\nJava_com_github_kr328_clash_core_bridge_Bridge_nativeSubscribeLogcat(JNIEnv *env, jobject thiz,\n                                                                     jobject callback) {\n    TRACE_METHOD();\n\n    jobject _callback = new_global(callback);\n\n    subscribeLogcat(_callback);\n}\n\nstatic jmethodID m_tun_interface_mark_socket;\nstatic jmethodID m_tun_interface_query_socket_uid;\nstatic jmethodID m_completable_complete;\nstatic jmethodID m_completable_complete_exceptionally;\nstatic jmethodID m_logcat_interface_received;\nstatic jmethodID m_clash_exception;\nstatic jmethodID m_fetch_callback_report;\nstatic jmethodID m_fetch_callback_complete;\nstatic jmethodID m_open;\nstatic jmethodID m_get_message;\nstatic jclass c_clash_exception;\nstatic jclass c_content;\nstatic jobject o_unit;\n\nstatic void call_tun_interface_mark_socket_impl(void *tun_interface, int fd) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    (*env)->CallVoidMethod(env, (jobject) tun_interface,\n                           (jmethodID) m_tun_interface_mark_socket,\n                           (jint) fd);\n}\n\nstatic int call_tun_interface_query_socket_uid_impl(void *tun_interface, int protocol,\n                                                    const char *source, const char *target) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    return (*env)->CallIntMethod(env, (jobject) tun_interface,\n                                 (jmethodID) m_tun_interface_query_socket_uid,\n                                 (jint) protocol,\n                                 (jstring) new_string(source),\n                                 (jstring) new_string(target));\n}\n\nstatic void call_completable_complete_impl(void *completable, const char *exception) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    if (exception == NULL) {\n        (*env)->CallBooleanMethod(env,\n                                  (jobject) completable,\n                                  (jmethodID) m_completable_complete,\n                                  (jobject) o_unit);\n    } else {\n        jthrowable _exception = (jthrowable)\n                (*env)->NewObject(env,\n                                  (jclass) c_clash_exception,\n                                  (jmethodID) m_clash_exception,\n                                  (jstring) new_string(exception)\n                );\n\n        (*env)->CallBooleanMethod(env,\n                                  (jobject) completable,\n                                  (jmethodID) m_completable_complete_exceptionally,\n                                  (jobject) _exception);\n    }\n}\n\nstatic void call_fetch_callback_report_impl(void *fetch_callback, const char *status_json) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    jstring _status_json = new_string(status_json);\n\n    (*env)->CallVoidMethod(env,\n                           (jobject) fetch_callback,\n                           (jmethodID) m_fetch_callback_report,\n                           (jstring) _status_json);\n}\n\nstatic void call_fetch_callback_complete_impl(void *fetch_callback, const char *error) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    jstring _error = NULL;\n\n    if (error != NULL)\n        _error = new_string(error);\n\n    (*env)->CallVoidMethod(env,\n                           (jobject) fetch_callback,\n                           (jmethodID) m_fetch_callback_complete,\n                           (jstring) _error);\n}\n\nstatic int call_logcat_interface_received_impl(void *callback, const char *payload) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    (*env)->CallVoidMethod(env,\n                           (jobject) callback,\n                           (jmethodID) m_logcat_interface_received,\n                           (jstring) new_string(payload));\n\n    if (jni_catch_exception(env)) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int open_content_impl(const char *url, char *error, int error_length) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    int fd = (*env)->CallStaticIntMethod(env, c_content, m_open, new_string(url));\n\n    if ((*env)->ExceptionCheck(env)) {\n        jthrowable exception = (*env)->ExceptionOccurred(env);\n\n        (*env)->ExceptionClear(env);\n\n        jstring message = (jstring) (*env)->CallObjectMethod(\n                env,\n                (jthrowable) exception,\n                (jmethodID) m_get_message\n        );\n\n        if (message == NULL) {\n            strncpy(error, \"unknown\", error_length - 1);\n        } else {\n            scoped_string _message = get_string(message);\n\n            strncpy(error, _message, error_length - 1);\n        }\n\n        return -1;\n    }\n\n    return fd;\n}\n\nstatic void release_jni_object_impl(void *obj) {\n    TRACE_METHOD();\n\n    ATTACH_JNI();\n\n    del_global((jobject) obj);\n}\n\nJNIEXPORT jint JNICALL\nJNI_OnLoad(JavaVM *vm, void *reserved) {\n    TRACE_METHOD();\n\n    JNIEnv *env = NULL;\n\n    if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK)\n        return JNI_ERR;\n\n    initialize_jni(vm, env);\n\n    jclass c_tun_interface = find_class(\"com/github/kr328/clash/core/bridge/TunInterface\");\n    jclass c_completable = find_class(\"kotlinx/coroutines/CompletableDeferred\");\n    jclass c_fetch_callback = find_class(\"com/github/kr328/clash/core/bridge/FetchCallback\");\n    jclass c_logcat_interface = find_class(\"com/github/kr328/clash/core/bridge/LogcatInterface\");\n    jclass _c_clash_exception = find_class(\"com/github/kr328/clash/core/bridge/ClashException\");\n    jclass _c_content = find_class(\"com/github/kr328/clash/core/bridge/Content\");\n    jclass c_throwable = find_class(\"java/lang/Throwable\");\n    jclass c_unit = find_class(\"kotlin/Unit\");\n\n    m_tun_interface_mark_socket = find_method(c_tun_interface, \"markSocket\",\n                                              \"(I)V\");\n    m_tun_interface_query_socket_uid = find_method(c_tun_interface, \"querySocketUid\",\n                                                   \"(ILjava/lang/String;Ljava/lang/String;)I\");\n    m_completable_complete = find_method(c_completable, \"complete\",\n                                         \"(Ljava/lang/Object;)Z\");\n    m_fetch_callback_report = find_method(c_fetch_callback, \"report\",\n                                          \"(Ljava/lang/String;)V\");\n    m_fetch_callback_complete = find_method(c_fetch_callback, \"complete\",\n                                            \"(Ljava/lang/String;)V\");\n    m_completable_complete_exceptionally = find_method(c_completable, \"completeExceptionally\",\n                                                       \"(Ljava/lang/Throwable;)Z\");\n    m_logcat_interface_received = find_method(c_logcat_interface, \"received\",\n                                              \"(Ljava/lang/String;)V\");\n    m_clash_exception = find_method(_c_clash_exception, \"<init>\",\n                                    \"(Ljava/lang/String;)V\");\n    m_get_message = find_method(c_throwable, \"getMessage\",\n                                \"()Ljava/lang/String;\");\n    m_open = (*env)->GetStaticMethodID(env, _c_content, \"open\",\n                                       \"(Ljava/lang/String;)I\");\n\n    o_unit = (*env)->GetStaticObjectField(env, c_unit,\n                                          (*env)->GetStaticFieldID(env, c_unit, \"INSTANCE\",\n                                                                   \"Lkotlin/Unit;\"));\n\n    c_clash_exception = (jclass) new_global(_c_clash_exception);\n    c_content = (jclass) new_global(_c_content);\n    o_unit = new_global(o_unit);\n\n    mark_socket_func = &call_tun_interface_mark_socket_impl;\n    query_socket_uid_func = &call_tun_interface_query_socket_uid_impl;\n    complete_func = &call_completable_complete_impl;\n    fetch_report_func = &call_fetch_callback_report_impl;\n    fetch_complete_func = &call_fetch_callback_complete_impl;\n    logcat_received_func = &call_logcat_interface_received_impl;\n    open_content_func = &open_content_impl;\n    release_object_func = &release_jni_object_impl;\n\n    return JNI_VERSION_1_6;\n}"
  },
  {
    "path": "core/src/main/golang/go.mod",
    "content": "module cfa\n\ngo 1.18\n\nrequire (\n\tgithub.com/Dreamacro/clash v1.7.1\n\tgithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34\n\tgithub.com/dlclark/regexp2 v1.4.0\n\tgithub.com/miekg/dns v1.1.43\n\tgithub.com/oschwald/geoip2-golang v1.5.0\n\tgolang.org/x/sync v0.0.0-20210220032951-036812b2e83c\n\tgopkg.in/yaml.v2 v2.4.0\n)\n\nrequire (\n\tgithub.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect\n\tgithub.com/gofrs/uuid v4.0.0+incompatible // indirect\n\tgithub.com/gorilla/websocket v1.4.2 // indirect\n\tgithub.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac // indirect\n\tgithub.com/oschwald/maxminddb-golang v1.8.0 // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgolang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect\n\tgolang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect\n\tgolang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect\n\tgolang.org/x/text v0.3.6 // indirect\n)\n"
  },
  {
    "path": "core/src/main/golang/go.sum",
    "content": "github.com/Dreamacro/clash v1.7.1 h1:8iYYiyVf7ZAztwoFeTFihs5rI9Jjic0ZKmf05vQxzFU=\ngithub.com/Dreamacro/clash v1.7.1/go.mod h1:C9eLMAlDZSLrkdzGQdOHyeldwRjDbcGR2kaL80KdzGw=\ngithub.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=\ngithub.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=\ngithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34 h1:USCTqih5d1bUXUxWNS9ZD5Tx/lb0jXHEtRIIx/F9dMc=\ngithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34/go.mod h1:YR9wK13TgI5ww8iKWm91MHiSoHC7Oz0U4beCCmtXqLw=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=\ngithub.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=\ngithub.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=\ngithub.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=\ngithub.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=\ngithub.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac h1:IO6EfdRnPhxgKOsk9DbewdtQZHKZKnGlW7QCUttvNys=\ngithub.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=\ngithub.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=\ngithub.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=\ngithub.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=\ngithub.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=\ngithub.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=\ngithub.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=\ngithub.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=\ngithub.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=\ngithub.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=\ngithub.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=\ngithub.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=\ngithub.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=\ngithub.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=\ngolang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=\ngolang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "core/src/main/golang/native/all/imports.go",
    "content": "package all\n\nimport (\n\t_ \"cfa/native/app\"\n\t_ \"cfa/native/common\"\n\t_ \"cfa/native/config\"\n\t_ \"cfa/native/delegate\"\n\t_ \"cfa/native/platform\"\n\t_ \"cfa/native/proxy\"\n\t_ \"cfa/native/tun\"\n\t_ \"cfa/native/tunnel\"\n\n\t_ \"golang.org/x/sync/semaphore\"\n\n\t_ \"github.com/Dreamacro/clash/log\"\n)\n"
  },
  {
    "path": "core/src/main/golang/native/app/app.go",
    "content": "package app\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar appVersionName string\nvar platformVersion int\nvar installedAppsUid = map[int]string{}\n\nfunc ApplyVersionName(versionName string) {\n\tappVersionName = versionName\n}\n\nfunc ApplyPlatformVersion(version int) {\n\tplatformVersion = version\n}\n\nfunc VersionName() string {\n\treturn appVersionName\n}\n\nfunc PlatformVersion() int {\n\treturn platformVersion\n}\n\nfunc NotifyInstallAppsChanged(uidList string) {\n\tuids := map[int]string{}\n\n\tfor _, item := range strings.Split(uidList, \",\") {\n\t\tkv := strings.Split(item, \":\")\n\t\tif len(kv) == 2 {\n\t\t\tuid, err := strconv.Atoi(kv[0])\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tuids[uid] = kv[1]\n\t\t}\n\t}\n\n\tinstalledAppsUid = uids\n}\n\nfunc QueryAppByUid(uid int) string {\n\treturn installedAppsUid[uid]\n}\n\nfunc NotifyTimeZoneChanged(name string, offset int) {\n\ttime.Local = time.FixedZone(name, offset)\n}"
  },
  {
    "path": "core/src/main/golang/native/app/content.go",
    "content": "package app\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"syscall\"\n)\n\nvar openContentImpl = func(url string) (int, error) {\n\treturn -1, errors.New(\"not implement\")\n}\n\nfunc OpenContent(url string) (*os.File, error) {\n\tfd, err := openContentImpl(url)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t_ = syscall.SetNonblock(fd, true)\n\n\treturn os.NewFile(uintptr(fd), \"fd\"), nil\n}\n\nfunc ApplyContentContext(openContent func(string) (int, error)) {\n\topenContentImpl = openContent\n}\n"
  },
  {
    "path": "core/src/main/golang/native/app/dns.go",
    "content": "package app\n\nimport (\n\t\"strings\"\n\n\t\"github.com/Dreamacro/clash/dns\"\n)\n\nfunc NotifyDnsChanged(dnsList string) {\n\tdL := strings.Split(dnsList, \",\")\n\n\tns := make([]dns.NameServer, 0, len(dnsList))\n\tfor _, d := range dL {\n\t\tns = append(ns, dns.NameServer{Addr: d})\n\t}\n\n\tdns.UpdateSystemDNS(dL)\n\tdns.FlushCacheWithDefaultResolver()\n}\n\n"
  },
  {
    "path": "core/src/main/golang/native/app/tun.go",
    "content": "package app\n\nimport (\n\t\"net\"\n\t\"syscall\"\n\n\t\"cfa/native/platform\"\n)\n\nvar markSocketImpl func(fd int)\nvar querySocketUidImpl func(protocol int, source, target string) int\n\nfunc MarkSocket(fd int) {\n\tmarkSocketImpl(fd)\n}\n\nfunc QuerySocketUid(source, target net.Addr) int {\n\tvar protocol int\n\n\tswitch source.Network() {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\tprotocol = syscall.IPPROTO_UDP\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\tprotocol = syscall.IPPROTO_TCP\n\tdefault:\n\t\treturn -1\n\t}\n\n\tif PlatformVersion() < 29 {\n\t\treturn platform.QuerySocketUidFromProcFs(source, target)\n\t}\n\n\treturn querySocketUidImpl(protocol, source.String(), target.String())\n}\n\nfunc ApplyTunContext(markSocket func(fd int), querySocketUid func(int, string, string) int) {\n\tif markSocket == nil {\n\t\tmarkSocket = func(fd int) {}\n\t}\n\n\tif querySocketUid == nil {\n\t\tquerySocketUid = func(int, string, string) int { return -1 }\n\t}\n\n\tmarkSocketImpl = markSocket\n\tquerySocketUidImpl = querySocketUid\n}\n\nfunc init() {\n\tApplyTunContext(nil, nil)\n}\n"
  },
  {
    "path": "core/src/main/golang/native/app/ui.go",
    "content": "package app\n\nimport (\n\t\"github.com/dlclark/regexp2\"\n\n\t\"github.com/Dreamacro/clash/log\"\n)\n\nvar uiSubtitlePattern *regexp2.Regexp\n\nfunc ApplySubtitlePattern(pattern string) {\n\tif pattern == \"\" {\n\t\tuiSubtitlePattern = nil\n\n\t\treturn\n\t}\n\n\tif o := uiSubtitlePattern; o != nil && o.String() == pattern {\n\t\treturn\n\t}\n\n\treg, err := regexp2.Compile(pattern, regexp2.IgnoreCase|regexp2.Compiled)\n\tif err == nil {\n\t\tuiSubtitlePattern = reg\n\t} else {\n\t\tuiSubtitlePattern = nil\n\n\t\tlog.Warnln(\"Compile ui-subtitle-pattern: %s\", err.Error())\n\t}\n}\n\nfunc SubtitlePattern() *regexp2.Regexp {\n\treturn uiSubtitlePattern\n}\n"
  },
  {
    "path": "core/src/main/golang/native/app.go",
    "content": "package main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"errors\"\n\t\"unsafe\"\n\n\t\"cfa/native/app\"\n\n\t\"github.com/Dreamacro/clash/log\"\n)\n\nfunc openRemoteContent(url string) (int, error) {\n\tu := C.CString(url)\n\te := (*C.char)(C.malloc(1024))\n\n\tlog.Debugln(\"Open remote url: %s\", url)\n\n\tdefer C.free(unsafe.Pointer(e))\n\n\tfd := C.open_content(u, e, 1024)\n\n\tif fd < 0 {\n\t\treturn -1, errors.New(C.GoString(e))\n\t}\n\n\treturn int(fd), nil\n}\n\n//export notifyDnsChanged\nfunc notifyDnsChanged(dnsList C.c_string) {\n\td := C.GoString(dnsList)\n\n\tapp.NotifyDnsChanged(d)\n}\n\n//export notifyInstalledAppsChanged\nfunc notifyInstalledAppsChanged(uids C.c_string) {\n\tu := C.GoString(uids)\n\n\tapp.NotifyInstallAppsChanged(u)\n}\n\n//export notifyTimeZoneChanged\nfunc notifyTimeZoneChanged(name C.c_string, offset C.int) {\n\tapp.NotifyTimeZoneChanged(C.GoString(name), int(offset))\n}\n\n\n//export queryConfiguration\nfunc queryConfiguration() *C.char {\n\tresponse := &struct{}{}\n\n\treturn marshalJson(&response)\n}\n\nfunc init() {\n\tapp.ApplyContentContext(openRemoteContent)\n}"
  },
  {
    "path": "core/src/main/golang/native/bridge.c",
    "content": "#include \"bridge.h\"\n#include \"trace.h\"\n\nvoid (*mark_socket_func)(void *tun_interface, int fd);\n\nint (*query_socket_uid_func)(void *tun_interface, int protocol, const char *source, const char *target);\n\nvoid (*complete_func)(void *completable, const char *exception);\n\nvoid (*fetch_report_func)(void *fetch_callback, const char *status_json);\n\nvoid (*fetch_complete_func)(void *fetch_callback, const char *error);\n\nint (*logcat_received_func)(void *logcat_interface, const char *payload);\n\nint (*open_content_func)(const char *url, char *error, int error_length);\n\nvoid (*release_object_func)(void *obj);\n\nvoid mark_socket(void *interface, int fd) {\n    TRACE_METHOD();\n\n    mark_socket_func(interface, fd);\n}\n\nint query_socket_uid(void *interface, int protocol, char *source, char *target) {\n    TRACE_METHOD();\n\n    int result = query_socket_uid_func(interface, protocol, source, target);\n\n    free(source);\n    free(target);\n\n    return result;\n}\n\nvoid complete(void *obj, char *error) {\n    TRACE_METHOD();\n\n    complete_func(obj, error);\n\n    free(error);\n}\n\nvoid fetch_complete(void *fetch_callback, char *exception) {\n    TRACE_METHOD();\n\n    fetch_complete_func(fetch_callback, exception);\n\n    free(exception);\n}\n\nvoid fetch_report(void *fetch_callback, char *json_status) {\n    TRACE_METHOD();\n\n    fetch_report_func(fetch_callback, json_status);\n\n    free(json_status);\n}\n\nint logcat_received(void *logcat_interface, char *payload) {\n    TRACE_METHOD();\n\n    int result = logcat_received_func(logcat_interface, payload);\n\n    free(payload);\n\n    return result;\n}\n\nint open_content(char *url, char *error, int error_length) {\n    TRACE_METHOD();\n\n    int result = open_content_func(url, error, error_length);\n\n    free(url);\n\n    return result;\n}\n\nvoid release_object(void *obj) {\n    TRACE_METHOD();\n\n    release_object_func(obj);\n}\n\nvoid log_info(char *msg) {\n    __android_log_write(ANDROID_LOG_INFO, TAG, msg);\n\n    free(msg);\n}\n\nvoid log_error(char *msg) {\n    __android_log_write(ANDROID_LOG_ERROR, TAG, msg);\n\n    free(msg);\n}\n\nvoid log_warn(char *msg) {\n    __android_log_write(ANDROID_LOG_WARN, TAG, msg);\n\n    free(msg);\n}\n\nvoid log_debug(char *msg) {\n    __android_log_write(ANDROID_LOG_DEBUG, TAG, msg);\n\n    free(msg);\n}\n\nvoid log_verbose(char *msg) {\n    __android_log_write(ANDROID_LOG_VERBOSE, TAG, msg);\n\n    free(msg);\n}\n"
  },
  {
    "path": "core/src/main/golang/native/bridge.h",
    "content": "#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n#include <malloc.h>\n#include <android/log.h>\n\n#define TAG \"ClashForAndroid\"\n\ntypedef const char *c_string;\n\nextern void (*mark_socket_func)(void *tun_interface, int fd);\n\nextern int (*query_socket_uid_func)(void *tun_interface, int protocol, const char *source, const char *target);\n\nextern void (*complete_func)(void *completable, const char *exception);\n\nextern void (*fetch_report_func)(void *fetch_callback, const char *status_json);\n\nextern void (*fetch_complete_func)(void *fetch_callback, const char *error);\n\nextern int (*logcat_received_func)(void *logcat_interface, const char *payload);\n\nextern void (*release_object_func)(void *obj);\n\nextern int (*open_content_func)(const char *url, char *error, int error_length);\n\n// cgo\nextern void mark_socket(void *interface, int fd);\n\nextern int query_socket_uid(void *interface, int protocol, char *source, char *target);\n\nextern void complete(void *obj, char *error);\n\nextern void fetch_complete(void *completable, char *exception);\n\nextern void fetch_report(void *fetch_callback, char *status_json);\n\nextern int logcat_received(void *logcat_interface, char *payload);\n\nextern void release_object(void *obj);\n\nextern int open_content(char *url, char *error, int error_length);\n\nextern void log_info(char *msg);\n\nextern void log_error(char *msg);\n\nextern void log_warn(char *msg);\n\nextern void log_debug(char *msg);\n\nextern void log_verbose(char *msg);"
  },
  {
    "path": "core/src/main/golang/native/common/path.go",
    "content": "package common\n\nimport \"strings\"\n\nfunc ResolveAsRoot(path string) string {\n\tdirectories := strings.Split(path, \"/\")\n\tresult := make([]string, 0, len(directories))\n\n\tfor _, directory := range directories {\n\t\tswitch directory {\n\t\tcase \"\", \".\":\n\t\t\tcontinue\n\t\tcase \"..\":\n\t\t\tif len(result) > 0 {\n\t\t\t\tresult = result[:len(result)-1]\n\t\t\t}\n\t\tdefault:\n\t\t\tresult = append(result, directory)\n\t\t}\n\t}\n\n\treturn strings.Join(result, \"/\")\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/defaults.go",
    "content": "package config\n\nvar (\n\tdefaultNameServers = []string{\n\t\t\"223.5.5.5\",\n\t\t\"119.29.29.29\",\n\t\t\"8.8.4.4\",\n\t\t\"1.0.0.1\",\n\t}\n\tdefaultFakeIPFilter = []string{\n\t\t// Stun Services\n\t\t\"+.stun.*.*\",\n\t\t\"+.stun.*.*.*\",\n\t\t\"+.stun.*.*.*.*\",\n\t\t\"+.stun.*.*.*.*.*\",\n\n\t\t// Google Voices\n\t\t\"lens.l.google.com\",\n\n\t\t// Nintendo Switch STUN\n\t\t\"*.n.n.srv.nintendo.net\",\n\n\t\t// PlayStation STUN\n\t\t\"+.stun.playstation.net\",\n\n\t\t// XBox\n\t\t\"xbox.*.*.microsoft.com\",\n\t\t\"*.*.xboxlive.com\",\n\n\t\t// Microsoft Captive Portal\n\t\t\"*.msftncsi.com\",\n\t\t\"*.msftconnecttest.com\",\n\n\t\t// Bilibili CDN\n\t\t\"*.mcdn.bilivideo.cn\",\n\n\t\t// Windows Default LAN WorkGroup\n\t\t\"WORKGROUP\",\n\t}\n\tdefaultFakeIPRange = \"28.0.0.0/8\"\n)\n"
  },
  {
    "path": "core/src/main/golang/native/config/fetch.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\tU \"net/url\"\n\t\"os\"\n\tP \"path\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"cfa/native/app\"\n\t\"github.com/Dreamacro/clash/component/dialer\"\n)\n\ntype Status struct {\n\tAction      string   `json:\"action\"`\n\tArgs        []string `json:\"args\"`\n\tProgress    int      `json:\"progress\"`\n\tMaxProgress int      `json:\"max\"`\n}\n\nvar client = &http.Client{\n\tTransport: &http.Transport{\n\t\tDisableKeepAlives:     true,\n\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\tExpectContinueTimeout: 1 * time.Second,\n\t\tDialContext:           dialer.DialTunnelContext,\n\t},\n\tTimeout: 60 * time.Second,\n}\n\nfunc openUrl(url string) (io.ReadCloser, error) {\n\trequest, err := http.NewRequest(http.MethodGet, url, nil)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trequest.Header.Set(\"User-Agent\", \"ClashForAndroid/\"+app.VersionName())\n\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn response.Body, nil\n}\n\nfunc openContent(url string) (io.ReadCloser, error) {\n\treturn app.OpenContent(url)\n}\n\nfunc fetch(url *U.URL, file string) error {\n\tvar reader io.ReadCloser\n\tvar err error\n\n\tswitch url.Scheme {\n\tcase \"http\", \"https\":\n\t\treader, err = openUrl(url.String())\n\tcase \"content\":\n\t\treader, err = openContent(url.String())\n\tdefault:\n\t\terr = fmt.Errorf(\"unsupported scheme %s of %s\", url.Scheme, url)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer reader.Close()\n\n\t_ = os.MkdirAll(P.Dir(file), 0700)\n\n\tf, err := os.OpenFile(file, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer f.Close()\n\n\t_, err = io.Copy(f, reader)\n\tif err != nil {\n\t\t_ = os.Remove(file)\n\t}\n\n\treturn err\n}\n\nfunc FetchAndValid(\n\tpath string,\n\turl string,\n\tforce bool,\n\treportStatus func(string),\n) error {\n\tconfigPath := P.Join(path, \"config.yaml\")\n\n\tif _, err := os.Stat(configPath); os.IsNotExist(err) || force {\n\t\turl, err := U.Parse(url)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tbytes, _ := json.Marshal(&Status{\n\t\t\tAction:      \"FetchConfiguration\",\n\t\t\tArgs:        []string{url.Host},\n\t\t\tProgress:    -1,\n\t\t\tMaxProgress: -1,\n\t\t})\n\n\t\treportStatus(string(bytes))\n\n\t\tif err := fetch(url, configPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tdefer runtime.GC()\n\n\trawCfg, err := UnmarshalAndPatch(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tforEachProviders(rawCfg, func(index int, total int, name string, provider map[string]any) {\n\t\tbytes, _ := json.Marshal(&Status{\n\t\t\tAction:      \"FetchProviders\",\n\t\t\tArgs:        []string{name},\n\t\t\tProgress:    index,\n\t\t\tMaxProgress: total,\n\t\t})\n\n\t\treportStatus(string(bytes))\n\n\t\tu, uok := provider[\"url\"]\n\t\tp, pok := provider[\"path\"]\n\n\t\tif !uok || !pok {\n\t\t\treturn\n\t\t}\n\n\t\tus, uok := u.(string)\n\t\tps, pok := p.(string)\n\n\t\tif !uok || !pok {\n\t\t\treturn\n\t\t}\n\n\t\tif _, err := os.Stat(ps); err == nil {\n\t\t\treturn\n\t\t}\n\n\t\turl, err := U.Parse(us)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t_ = fetch(url, ps)\n\t})\n\n\tbytes, _ := json.Marshal(&Status{\n\t\tAction:      \"Verifying\",\n\t\tArgs:        []string{},\n\t\tProgress:    0xffff,\n\t\tMaxProgress: 0xffff,\n\t})\n\n\treportStatus(string(bytes))\n\n\tcfg, err := Parse(rawCfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdestroyProviders(cfg)\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/load.go",
    "content": "package config\n\nimport (\n\t\"io/ioutil\"\n\tP \"path\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"gopkg.in/yaml.v2\"\n\n\t\"cfa/native/app\"\n\t\"github.com/Dreamacro/clash/log\"\n\n\t\"github.com/Dreamacro/clash/config\"\n\t\"github.com/Dreamacro/clash/hub/executor\"\n)\n\nfunc logDns(cfg *config.RawConfig) {\n\tbytes, err := yaml.Marshal(&cfg.DNS)\n\tif err != nil {\n\t\tlog.Warnln(\"Marshal dns: %s\", err.Error())\n\n\t\treturn\n\t}\n\n\tlog.Infoln(\"dns:\")\n\n\tfor _, line := range strings.Split(string(bytes), \"\\n\") {\n\t\tlog.Infoln(\"  %s\", line)\n\t}\n}\n\nfunc UnmarshalAndPatch(profilePath string) (*config.RawConfig, error) {\n\tconfigPath := P.Join(profilePath, \"config.yaml\")\n\n\tconfigData, err := ioutil.ReadFile(configPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trawConfig, err := config.UnmarshalRawConfig(configData)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := process(rawConfig, profilePath); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn rawConfig, nil\n}\n\nfunc Parse(rawConfig *config.RawConfig) (*config.Config, error) {\n\tcfg, err := config.ParseRawConfig(rawConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn cfg, nil\n}\n\nfunc Load(path string) error {\n\trawCfg, err := UnmarshalAndPatch(path)\n\tif err != nil {\n\t\tlog.Errorln(\"Load %s: %s\", path, err.Error())\n\n\t\treturn err\n\t}\n\n\tlogDns(rawCfg)\n\n\tcfg, err := Parse(rawCfg)\n\tif err != nil {\n\t\tlog.Errorln(\"Load %s: %s\", path, err.Error())\n\n\t\treturn err\n\t}\n\n\texecutor.ApplyConfig(cfg, true)\n\n\tapp.ApplySubtitlePattern(rawCfg.ClashForAndroid.UiSubtitlePattern)\n\n\truntime.GC()\n\n\treturn nil\n}\n\nfunc LoadDefault() {\n\tcfg, err := config.Parse([]byte{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\texecutor.ApplyConfig(cfg, true)\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/override.go",
    "content": "package config\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\n\t\"github.com/Dreamacro/clash/constant\"\n)\n\ntype OverrideSlot int\n\nconst (\n\tOverrideSlotPersist OverrideSlot = iota\n\tOverrideSlotSession\n)\n\nconst defaultPersistOverride = `{\"dns\":{\"enable\": false}, \"redir-port\": 0, \"tproxy-port\": 0}`\nconst defaultSessionOverride = `{}`\n\nvar sessionOverride = defaultSessionOverride\n\nfunc overridePersistPath() string {\n\treturn constant.Path.Resolve(\"override.json\")\n}\n\nfunc ReadOverride(slot OverrideSlot) string {\n\tswitch slot {\n\tcase OverrideSlotPersist:\n\t\tfile, err := os.OpenFile(overridePersistPath(), os.O_RDONLY, 0600)\n\t\tif err != nil {\n\t\t\treturn defaultPersistOverride\n\t\t}\n\n\t\tbuf, err := ioutil.ReadAll(file)\n\t\tif err != nil {\n\t\t\treturn defaultPersistOverride\n\t\t}\n\n\t\treturn string(buf)\n\tcase OverrideSlotSession:\n\t\treturn sessionOverride\n\t}\n\n\treturn \"\"\n}\n\nfunc WriteOverride(slot OverrideSlot, content string) {\n\tswitch slot {\n\tcase OverrideSlotPersist:\n\t\tfile, err := os.OpenFile(overridePersistPath(), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t_, err = file.Write([]byte(content))\n\tcase OverrideSlotSession:\n\t\tsessionOverride = content\n\t}\n}\n\nfunc ClearOverride(slot OverrideSlot) {\n\tswitch slot {\n\tcase OverrideSlotPersist:\n\t\t_ = os.Remove(overridePersistPath())\n\tcase OverrideSlotSession:\n\t\tsessionOverride = defaultSessionOverride\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/process.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/dlclark/regexp2\"\n\n\t\"cfa/native/common\"\n\tC \"github.com/Dreamacro/clash/constant\"\n\t\"github.com/Dreamacro/clash/log\"\n\n\t\"github.com/Dreamacro/clash/config\"\n\t\"github.com/Dreamacro/clash/dns\"\n)\n\nvar processors = []processor{\n\tpatchOverride,\n\tpatchGeneral,\n\tpatchProfile,\n\tpatchDns,\n\tpatchProviders,\n\tpatchTun,\n\tvalidConfig,\n}\n\ntype processor func(cfg *config.RawConfig, profileDir string) error\n\nfunc patchOverride(cfg *config.RawConfig, _ string) error {\n\tif err := json.NewDecoder(strings.NewReader(ReadOverride(OverrideSlotPersist))).Decode(cfg); err != nil {\n\t\tlog.Warnln(\"Apply persist override: %s\", err.Error())\n\t}\n\tif err := json.NewDecoder(strings.NewReader(ReadOverride(OverrideSlotSession))).Decode(cfg); err != nil {\n\t\tlog.Warnln(\"Apply session override: %s\", err.Error())\n\t}\n\n\treturn nil\n}\n\nfunc patchGeneral(cfg *config.RawConfig, _ string) error {\n\tcfg.Interface = \"\"\n\tcfg.ExternalUI = \"\"\n\tcfg.ExternalController = \"\"\n\n\treturn nil\n}\n\nfunc patchProfile(cfg *config.RawConfig, _ string) error {\n\tcfg.Profile.StoreSelected = false\n\tcfg.Profile.StoreFakeIP = true\n\n\treturn nil\n}\n\nfunc patchDns(cfg *config.RawConfig, _ string) error {\n\tif !cfg.DNS.Enable {\n\t\tcfg.DNS = config.RawDNS{\n\t\t\tEnable:            true,\n\t\t\tUseHosts:          true,\n\t\t\tDefaultNameserver: defaultNameServers,\n\t\t\tNameServer:        defaultNameServers,\n\t\t\tEnhancedMode:      C.DNSFakeIP,\n\t\t\tFakeIPRange:       defaultFakeIPRange,\n\t\t\tFakeIPFilter:      defaultFakeIPFilter,\n\t\t}\n\n\t\tcfg.ClashForAndroid.AppendSystemDNS = true\n\t}\n\n\tif cfg.ClashForAndroid.AppendSystemDNS {\n\t\tcfg.DNS.NameServer = append(cfg.DNS.NameServer, \"dhcp://\"+dns.SystemDNSPlaceholder)\n\t}\n\n\treturn nil\n}\n\nfunc patchProviders(cfg *config.RawConfig, profileDir string) error {\n\tforEachProviders(cfg, func(index int, total int, key string, provider map[string]any) {\n\t\tif path, ok := provider[\"path\"].(string); ok {\n\t\t\tprovider[\"path\"] = profileDir + \"/providers/\" + common.ResolveAsRoot(path)\n\t\t}\n\t})\n\n\treturn nil\n}\n\nfunc validConfig(cfg *config.RawConfig, _ string) error {\n\tif len(cfg.Proxy) == 0 && len(cfg.ProxyProvider) == 0 {\n\t\treturn errors.New(\"profile does not contain `proxies` or `proxy-providers`\")\n\t}\n\n\tif _, err := regexp2.Compile(cfg.ClashForAndroid.UiSubtitlePattern, 0); err != nil {\n\t\treturn fmt.Errorf(\"compile ui-subtitle-pattern: %s\", err.Error())\n\t}\n\n\treturn nil\n}\n\nfunc process(cfg *config.RawConfig, profileDir string) error {\n\tfor _, p := range processors {\n\t\tif err := p(cfg, profileDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/process_open.go",
    "content": "//go:build !premium\n\npackage config\n\nimport \"github.com/Dreamacro/clash/config\"\n\nfunc patchTun(cfg *config.RawConfig, _ string) error {\n\treturn nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/process_premium.go",
    "content": "//go:build premium\n\npackage config\n\nimport \"github.com/Dreamacro/clash/config\"\n\nfunc patchTun(cfg *config.RawConfig, _ string) error {\n\tcfg.Tun.Enable = false\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/provider_open.go",
    "content": "//go:build !premium\n\npackage config\n\nimport (\n\t\"io\"\n\n\t\"github.com/Dreamacro/clash/config\"\n)\n\nfunc forEachProviders(rawCfg *config.RawConfig, fun func(index int, total int, key string, provider map[string]any)) {\n\ttotal := len(rawCfg.ProxyProvider)\n\tindex := 0\n\n\tfor k, v := range rawCfg.ProxyProvider {\n\t\tfun(index, total, k, v)\n\n\t\tindex++\n\t}\n}\n\nfunc destroyProviders(cfg *config.Config) {\n\tfor _, p := range cfg.Providers {\n\t\t_ = p.(io.Closer).Close()\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config/provider_premium.go",
    "content": "//go:build premium\n\npackage config\n\nimport (\n\t\"io\"\n\n\t\"github.com/Dreamacro/clash/config\"\n)\n\nfunc forEachProviders(rawCfg *config.RawConfig, fun func(index int, total int, key string, provider map[string]any)) {\n\ttotal := len(rawCfg.ProxyProvider) + len(rawCfg.RuleProvider)\n\tindex := 0\n\n\tfor k, v := range rawCfg.ProxyProvider {\n\t\tfun(index, total, k, v)\n\n\t\tindex++\n\t}\n\n\tfor k, v := range rawCfg.RuleProvider {\n\t\tfun(index, total, k, v)\n\n\t\tindex++\n\t}\n}\n\nfunc destroyProviders(cfg *config.Config) {\n\tfor _, p := range cfg.ProxyProviders {\n\t\t_ = p.(io.Closer).Close()\n\t}\n\n\tfor _, p := range cfg.RuleProviders {\n\t\t_ = p.(io.Closer).Close()\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/config.go",
    "content": "package main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"cfa/native/config\"\n)\n\ntype remoteValidCallback struct {\n\tcallback unsafe.Pointer\n}\n\nfunc (r *remoteValidCallback) reportStatus(json string) {\n\tC.fetch_report(r.callback, marshalString(json))\n}\n\n//export fetchAndValid\nfunc fetchAndValid(callback unsafe.Pointer, path, url C.c_string, force C.int) {\n\tgo func(path, url string, callback unsafe.Pointer) {\n\t\tcb := &remoteValidCallback{callback: callback}\n\n\t\terr := config.FetchAndValid(path, url, force != 0, cb.reportStatus)\n\n\t\tC.fetch_complete(callback, marshalString(err))\n\n\t\tC.release_object(callback)\n\n\t\truntime.GC()\n\t}(C.GoString(path), C.GoString(url), callback)\n}\n\n//export load\nfunc load(completable unsafe.Pointer, path C.c_string) {\n\tgo func(path string) {\n\t\tC.complete(completable, marshalString(config.Load(path)))\n\n\t\tC.release_object(completable)\n\n\t\truntime.GC()\n\t}(C.GoString(path))\n}\n\n//export readOverride\nfunc readOverride(slot C.int) *C.char {\n\treturn C.CString(config.ReadOverride(config.OverrideSlot(slot)))\n}\n\n//export writeOverride\nfunc writeOverride(slot C.int, content C.c_string) {\n\tc := C.GoString(content)\n\n\tconfig.WriteOverride(config.OverrideSlot(slot), c)\n}\n\n//export clearOverride\nfunc clearOverride(slot C.int) {\n\tconfig.ClearOverride(config.OverrideSlot(slot))\n}"
  },
  {
    "path": "core/src/main/golang/native/debug.go",
    "content": "// +build debug\n\npackage main\n\nimport (\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\n\t\"github.com/Dreamacro/clash/log\"\n)\n\nfunc init() {\n\tgo func() {\n\t\tlog.Debugln(\"pprof service listen at: 0.0.0.0:8888\")\n\n\t\t_ = http.ListenAndServe(\"0.0.0.0:8888\", nil)\n\t}()\n}\n"
  },
  {
    "path": "core/src/main/golang/native/delegate/init.go",
    "content": "package delegate\n\nimport (\n\t\"errors\"\n\t\"syscall\"\n\n\t\"cfa/blob\"\n\n\t\"github.com/Dreamacro/clash/component/process\"\n\t\"github.com/Dreamacro/clash/log\"\n\n\t\"cfa/native/app\"\n\t\"cfa/native/platform\"\n\n\t\"github.com/Dreamacro/clash/component/dialer\"\n\t\"github.com/Dreamacro/clash/component/mmdb\"\n\t\"github.com/Dreamacro/clash/constant\"\n)\n\nvar errBlocked = errors.New(\"blocked\")\n\nfunc Init(home, versionName string, platformVersion int) {\n\tmmdb.LoadFromBytes(blob.GeoipDatabase)\n\tconstant.SetHomeDir(home)\n\tapp.ApplyVersionName(versionName)\n\tapp.ApplyPlatformVersion(platformVersion)\n\n\tprocess.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) {\n\t\tsrc, dst := metadata.RawSrcAddr, metadata.RawDstAddr\n\n\t\tif src == nil || dst == nil {\n\t\t\treturn \"\", process.ErrInvalidNetwork\n\t\t}\n\n\t\tuid := app.QuerySocketUid(metadata.RawSrcAddr, metadata.RawDstAddr)\n\t\tpkg := app.QueryAppByUid(uid)\n\n\t\tlog.Debugln(\"[PKG] %s --> %s by %d[%s]\", metadata.SourceAddress(), metadata.RemoteAddress(), uid, pkg)\n\n\t\treturn pkg, nil\n\t}\n\n\tdialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {\n\t\tif platform.ShouldBlockConnection() {\n\t\t\treturn errBlocked\n\t\t}\n\n\t\treturn conn.Control(func(fd uintptr) {\n\t\t\tapp.MarkSocket(int(fd))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/log_open.go",
    "content": "//go:build !premium\n\npackage main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"strings\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/Dreamacro/clash/log\"\n)\n\ntype message struct {\n\tLevel   string `json:\"level\"`\n\tMessage string `json:\"message\"`\n\tTime    int64  `json:\"time\"`\n}\n\nfunc init() {\n\tgo func() {\n\t\tsub := log.Subscribe()\n\t\tdefer log.UnSubscribe(sub)\n\n\t\tfor item := range sub {\n\t\t\tmsg := item.(log.Event)\n\n\t\t\tcPayload := C.CString(msg.Payload)\n\n\t\t\tswitch msg.LogLevel {\n\t\t\tcase log.INFO:\n\t\t\t\tC.log_info(cPayload)\n\t\t\tcase log.ERROR:\n\t\t\t\tC.log_error(cPayload)\n\t\t\tcase log.WARNING:\n\t\t\t\tC.log_warn(cPayload)\n\t\t\tcase log.DEBUG:\n\t\t\t\tC.log_debug(cPayload)\n\t\t\tcase log.SILENT:\n\t\t\t\tC.log_verbose(cPayload)\n\t\t\t}\n\t\t}\n\t}()\n}\n\n//export subscribeLogcat\nfunc subscribeLogcat(remote unsafe.Pointer) {\n\tgo func(remote unsafe.Pointer) {\n\t\tsub := log.Subscribe()\n\t\tdefer log.UnSubscribe(sub)\n\n\t\tfor i := range sub {\n\t\t\tmsg, ok := i.(log.Event)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif msg.LogLevel < log.Level() && !strings.HasPrefix(msg.Payload, \"[APP]\") {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trMsg := &message{\n\t\t\t\tLevel:   msg.LogLevel.String(),\n\t\t\t\tMessage: msg.Payload,\n\t\t\t\tTime:    time.Now().UnixNano() / 1000 / 1000,\n\t\t\t}\n\n\t\t\tif C.logcat_received(remote, marshalJson(rMsg)) != 0 {\n\t\t\t\tC.release_object(remote)\n\n\t\t\t\tlog.Debugln(\"Logcat subscriber closed\")\n\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}(remote)\n\n\tlog.Infoln(\"[APP] Logcat level: %s\", log.Level().String())\n}\n"
  },
  {
    "path": "core/src/main/golang/native/log_premium.go",
    "content": "//go:build premium\n\npackage main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"strings\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/Dreamacro/clash/log\"\n)\n\ntype message struct {\n\tLevel   string `json:\"level\"`\n\tMessage string `json:\"message\"`\n\tTime    int64  `json:\"time\"`\n}\n\nfunc init() {\n\tgo func() {\n\t\tsub := log.Subscribe()\n\t\tdefer log.UnSubscribe(sub)\n\n\t\tfor msg := range sub {\n\n\t\t\tcPayload := C.CString(msg.Payload)\n\n\t\t\tswitch msg.LogLevel {\n\t\t\tcase log.INFO:\n\t\t\t\tC.log_info(cPayload)\n\t\t\tcase log.ERROR:\n\t\t\t\tC.log_error(cPayload)\n\t\t\tcase log.WARNING:\n\t\t\t\tC.log_warn(cPayload)\n\t\t\tcase log.DEBUG:\n\t\t\t\tC.log_debug(cPayload)\n\t\t\tcase log.SILENT:\n\t\t\t\tC.log_verbose(cPayload)\n\t\t\t}\n\t\t}\n\t}()\n}\n\n//export subscribeLogcat\nfunc subscribeLogcat(remote unsafe.Pointer) {\n\tgo func(remote unsafe.Pointer) {\n\t\tsub := log.Subscribe()\n\t\tdefer log.UnSubscribe(sub)\n\n\t\tfor msg := range sub {\n\t\t\tif msg.LogLevel < log.Level() && !strings.HasPrefix(msg.Payload, \"[APP]\") {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trMsg := &message{\n\t\t\t\tLevel:   msg.LogLevel.String(),\n\t\t\t\tMessage: msg.Payload,\n\t\t\t\tTime:    time.Now().UnixNano() / 1000 / 1000,\n\t\t\t}\n\n\t\t\tif C.logcat_received(remote, marshalJson(rMsg)) != 0 {\n\t\t\t\tC.release_object(remote)\n\n\t\t\t\tlog.Debugln(\"Logcat subscriber closed\")\n\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}(remote)\n\n\tlog.Infoln(\"[APP] Logcat level: %s\", log.Level().String())\n}\n"
  },
  {
    "path": "core/src/main/golang/native/main.go",
    "content": "package main\n\n/*\n#cgo LDFLAGS: -llog\n\n#include \"bridge.h\"\n*/\nimport \"C\"\n\nimport (\n\t\"runtime\"\n\n\t\"cfa/native/config\"\n\t\"cfa/native/delegate\"\n\t\"cfa/native/tunnel\"\n\n\t\"github.com/Dreamacro/clash/log\"\n)\n\nfunc main() {\n\tpanic(\"Stub!\")\n}\n\n//export coreInit\nfunc coreInit(home, versionName C.c_string, sdkVersion C.int) {\n\th := C.GoString(home)\n\tv := C.GoString(versionName)\n\ts := int(sdkVersion)\n\n\tdelegate.Init(h, v, s)\n\n\treset()\n}\n\n//export reset\nfunc reset() {\n\tconfig.LoadDefault()\n\ttunnel.ResetStatistic()\n\ttunnel.CloseAllConnections()\n\n\truntime.GC()\n}\n\n//export forceGc\nfunc forceGc() {\n\tgo func() {\n\t\tlog.Infoln(\"[APP] request force GC\")\n\n\t\truntime.GC()\n\t}()\n}\n"
  },
  {
    "path": "core/src/main/golang/native/platform/limit.go",
    "content": "// +build linux\n\npackage platform\n\nimport \"syscall\"\n\nvar nullFd int\nvar maxFdCount int\n\nfunc init() {\n\tfd, err := syscall.Open(\"/dev/null\", syscall.O_WRONLY, 0644)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tnullFd = fd\n\n\tvar limit syscall.Rlimit\n\n\tif err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {\n\t\tmaxFdCount = 1024\n\t} else {\n\t\tmaxFdCount = int(limit.Cur)\n\t}\n\n\tmaxFdCount = maxFdCount / 4 * 3\n}\n\nfunc ShouldBlockConnection() bool {\n\tfd, err := syscall.Dup(nullFd)\n\tif err != nil {\n\t\treturn true\n\t}\n\n\t_ = syscall.Close(fd)\n\n\tif fd > maxFdCount {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "core/src/main/golang/native/platform/procfs.go",
    "content": "// +build linux\n\npackage platform\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unsafe\"\n)\n\nvar netIndexOfLocal = -1\nvar netIndexOfUid = -1\n\nvar nativeEndian binary.ByteOrder\n\nfunc QuerySocketUidFromProcFs(source, _ net.Addr) int {\n\tif netIndexOfLocal < 0 || netIndexOfUid < 0 {\n\t\treturn -1\n\t}\n\n\tnetwork := source.Network()\n\n\tif strings.HasSuffix(network, \"4\") || strings.HasSuffix(network, \"6\") {\n\t\tnetwork = network[:len(network)-1]\n\t}\n\n\tpath := \"/proc/net/\" + network\n\n\tvar sIP net.IP\n\tvar sPort int\n\n\tswitch s := source.(type) {\n\tcase *net.TCPAddr:\n\t\tsIP = s.IP\n\t\tsPort = s.Port\n\tcase *net.UDPAddr:\n\t\tsIP = s.IP\n\t\tsPort = s.Port\n\tdefault:\n\t\treturn -1\n\t}\n\n\tsIP = sIP.To16()\n\tif sIP == nil {\n\t\treturn -1\n\t}\n\n\tuid := doQuery(path+\"6\", sIP, sPort)\n\tif uid == -1 {\n\t\tsIP = sIP.To4()\n\t\tif sIP == nil {\n\t\t\treturn -1\n\t\t}\n\t\tuid = doQuery(path, sIP, sPort)\n\t}\n\n\treturn uid\n}\n\nfunc doQuery(path string, sIP net.IP, sPort int) int {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn -1\n\t}\n\n\tdefer file.Close()\n\n\treader := bufio.NewReader(file)\n\n\tvar bytes [2]byte\n\n\tbinary.BigEndian.PutUint16(bytes[:], uint16(sPort))\n\n\tlocal := fmt.Sprintf(\"%s:%s\", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:]))\n\n\tfor {\n\t\trow, _, err := reader.ReadLine()\n\t\tif err != nil {\n\t\t\treturn -1\n\t\t}\n\n\t\tfields := strings.Fields(string(row))\n\n\t\tif len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.EqualFold(local, fields[netIndexOfLocal]) {\n\t\t\tuid, err := strconv.Atoi(fields[netIndexOfUid])\n\t\t\tif err != nil {\n\t\t\t\treturn -1\n\t\t\t}\n\n\t\t\treturn uid\n\t\t}\n\t}\n}\n\nfunc nativeEndianIP(ip net.IP) []byte {\n\tresult := make([]byte, len(ip))\n\n\tfor i := 0; i < len(ip); i += 4 {\n\t\tvalue := binary.BigEndian.Uint32(ip[i:])\n\n\t\tnativeEndian.PutUint32(result[i:], value)\n\t}\n\n\treturn result\n}\n\nfunc init() {\n\tfile, err := os.Open(\"/proc/net/tcp\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdefer file.Close()\n\n\treader := bufio.NewReader(file)\n\n\theader, _, err := reader.ReadLine()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcolumns := strings.Fields(string(header))\n\n\tvar txQueue, rxQueue, tr, tmWhen bool\n\n\tfor idx, col := range columns {\n\t\toffset := 0\n\n\t\tif txQueue && rxQueue {\n\t\t\toffset--\n\t\t}\n\n\t\tif tr && tmWhen {\n\t\t\toffset--\n\t\t}\n\n\t\tswitch col {\n\t\tcase \"tx_queue\":\n\t\t\ttxQueue = true\n\t\tcase \"rx_queue\":\n\t\t\trxQueue = true\n\t\tcase \"tr\":\n\t\t\ttr = true\n\t\tcase \"tm->when\":\n\t\t\ttmWhen = true\n\t\tcase \"local_address\":\n\t\t\tnetIndexOfLocal = idx + offset\n\t\tcase \"uid\":\n\t\t\tnetIndexOfUid = idx + offset\n\t\t}\n\t}\n}\n\nfunc init() {\n\tvar x uint32 = 0x01020304\n\tif *(*byte)(unsafe.Pointer(&x)) == 0x01 {\n\t\tnativeEndian = binary.BigEndian\n\t} else {\n\t\tnativeEndian = binary.LittleEndian\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/proxy/http.go",
    "content": "package proxy\n\nimport (\n\t\"sync\"\n\n\t\"github.com/Dreamacro/clash/listener/http\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\nvar listener *http.Listener\nvar lock sync.Mutex\n\nfunc Start(listen string) (listenAt string, err error) {\n\tlock.Lock()\n\tdefer lock.Unlock()\n\n\tstopLocked()\n\n\tlistener, err = http.NewWithAuthenticate(listen, tunnel.TCPIn(), false)\n\tif err == nil {\n\t\tlistenAt = listener.Listener().Addr().String()\n\t}\n\n\treturn\n}\n\nfunc Stop() {\n\tlock.Lock()\n\tdefer lock.Unlock()\n\n\tstopLocked()\n}\n\nfunc stopLocked() {\n\tif listener != nil {\n\t\tlistener.Close()\n\t}\n\n\tlistener = nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/proxy.go",
    "content": "package main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"cfa/native/proxy\"\n)\n\n//export startHttp\nfunc startHttp(listenAt C.c_string) *C.char {\n\tl := C.GoString(listenAt)\n\n\tlisten, err := proxy.Start(l)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\treturn C.CString(listen)\n}\n\n//export stopHttp\nfunc stopHttp() {\n\tproxy.Stop()\n}"
  },
  {
    "path": "core/src/main/golang/native/trace.c",
    "content": "#include \"trace.h\"\n\n#if ENABLE_TRACE\n\nvoid trace_method_exit(const char **name) {\n    __android_log_print(ANDROID_LOG_VERBOSE, TAG, \"TRACE-OUT %s\", *name);\n}\n\n#endif"
  },
  {
    "path": "core/src/main/golang/native/trace.h",
    "content": "#pragma once\n\n#include \"bridge.h\"\n\n#include <android/log.h>\n\n#define ENABLE_TRACE 0\n\n#if ENABLE_TRACE\n\nextern void trace_method_exit(const char **name);\n\n#define TRACE_METHOD() __attribute__((cleanup(trace_method_exit))) const char *__method_name = __FUNCTION__; __android_log_print(ANDROID_LOG_VERBOSE, TAG, \"TRACE-IN  %s\", __method_name)\n\n#else\n\n#define TRACE_METHOD()\n\n#endif"
  },
  {
    "path": "core/src/main/golang/native/tun/dns.go",
    "content": "package tun\n\nimport (\n\t\"net\"\n\n\t\"github.com/Dreamacro/clash/dns\"\n\n\tD \"github.com/miekg/dns\"\n)\n\nfunc shouldHijackDns(dns net.IP, target net.IP, targetPort int) bool {\n\tif targetPort != 53 {\n\t\treturn false\n\t}\n\n\treturn net.IPv4zero.Equal(dns) || target.Equal(dns)\n}\n\nfunc relayDns(payload []byte) ([]byte, error) {\n\tmsg := &D.Msg{}\n\tif err := msg.Unpack(payload); err != nil {\n\t\treturn nil, err\n\t}\n\n\tr, err := dns.ServeDNSWithDefaultServer(msg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tr.SetRcode(msg, r.Rcode)\n\n\treturn r.Pack()\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tun/metadata_open.go",
    "content": "//go:build !premium\n\npackage tun\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\n\tC \"github.com/Dreamacro/clash/constant\"\n)\n\nfunc createMetadata(lAddr, rAddr *net.TCPAddr) *C.Metadata {\n\treturn &C.Metadata{\n\t\tNetWork:    C.TCP,\n\t\tType:       C.SOCKS5,\n\t\tSrcIP:      lAddr.IP,\n\t\tDstIP:      rAddr.IP,\n\t\tSrcPort:    strconv.Itoa(lAddr.Port),\n\t\tDstPort:    strconv.Itoa(rAddr.Port),\n\t\tAddrType:   C.AtypIPv4,\n\t\tHost:       \"\",\n\t\tRawSrcAddr: lAddr,\n\t\tRawDstAddr: rAddr,\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tun/metadata_premium.go",
    "content": "//go:build premium\n\npackage tun\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\tC \"github.com/Dreamacro/clash/constant\"\n)\n\nfunc createMetadata(lAddr, rAddr *net.TCPAddr) *C.Metadata {\n\tsrcAddr, _ := netip.AddrFromSlice(lAddr.IP)\n\tdstAddr, _ := netip.AddrFromSlice(rAddr.IP)\n\n\treturn &C.Metadata{\n\t\tNetWork:    C.TCP,\n\t\tType:       C.SOCKS5,\n\t\tSrcIP:      srcAddr,\n\t\tDstIP:      dstAddr,\n\t\tSrcPort:    strconv.Itoa(lAddr.Port),\n\t\tDstPort:    strconv.Itoa(rAddr.Port),\n\t\tAddrType:   C.AtypIPv4,\n\t\tHost:       \"\",\n\t\tRawSrcAddr: lAddr,\n\t\tRawDstAddr: rAddr,\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tun/tun.go",
    "content": "package tun\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/Kr328/tun2socket\"\n\n\t\"github.com/Dreamacro/clash/adapter/inbound\"\n\t\"github.com/Dreamacro/clash/common/pool\"\n\tC \"github.com/Dreamacro/clash/constant\"\n\t\"github.com/Dreamacro/clash/context\"\n\t\"github.com/Dreamacro/clash/log\"\n\t\"github.com/Dreamacro/clash/transport/socks5\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\nvar _, ipv4LoopBack, _ = net.ParseCIDR(\"127.0.0.0/8\")\n\nfunc Start(fd int, gateway, portal, dns string) (io.Closer, error) {\n\tlog.Debugln(\"TUN: fd = %d, gateway = %s, portal = %s, dns = %s\", fd, gateway, portal, dns)\n\n\tdevice := os.NewFile(uintptr(fd), \"/dev/tun\")\n\n\tip, network, err := net.ParseCIDR(gateway)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t} else {\n\t\tnetwork.IP = ip\n\t}\n\n\tstack, err := tun2socket.StartTun2Socket(device, network, net.ParseIP(portal))\n\tif err != nil {\n\t\t_ = device.Close()\n\n\t\treturn nil, err\n\t}\n\n\tdnsAddr := net.ParseIP(dns)\n\n\ttcp := func() {\n\t\tdefer stack.TCP().Close()\n\t\tdefer log.Debugln(\"TCP: closed\")\n\n\t\tfor stack.TCP().SetDeadline(time.Time{}) == nil {\n\t\t\tconn, err := stack.TCP().Accept()\n\t\t\tif err != nil {\n\t\t\t\tlog.Debugln(\"Accept connection: %v\", err)\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tlAddr := conn.LocalAddr().(*net.TCPAddr)\n\t\t\trAddr := conn.RemoteAddr().(*net.TCPAddr)\n\n\t\t\tif ipv4LoopBack.Contains(rAddr.IP) {\n\t\t\t\tconn.Close()\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif shouldHijackDns(dnsAddr, rAddr.IP, rAddr.Port) {\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer conn.Close()\n\n\t\t\t\t\tbuf := pool.Get(pool.UDPBufferSize)\n\t\t\t\t\tdefer pool.Put(buf)\n\n\t\t\t\t\tfor {\n\t\t\t\t\t\tconn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout))\n\n\t\t\t\t\t\tlength := uint16(0)\n\t\t\t\t\t\tif err := binary.Read(conn, binary.BigEndian, &length); err != nil {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif int(length) > len(buf) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tn, err := conn.Read(buf[:length])\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmsg, err := relayDns(buf[:n])\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_, _ = conn.Write(msg)\n\t\t\t\t\t}\n\t\t\t\t}()\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ttunnel.TCPIn() <- context.NewConnContext(conn, createMetadata(lAddr, rAddr))\n\t\t}\n\t}\n\n\tudp := func() {\n\t\tdefer stack.UDP().Close()\n\t\tdefer log.Debugln(\"UDP: closed\")\n\n\t\tfor {\n\t\t\tbuf := pool.Get(pool.UDPBufferSize)\n\n\t\t\tn, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\traw := buf[:n]\n\t\t\tlAddr := lRAddr.(*net.UDPAddr)\n\t\t\trAddr := rRAddr.(*net.UDPAddr)\n\n\t\t\tif ipv4LoopBack.Contains(rAddr.IP) {\n\t\t\t\tpool.Put(buf)\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif shouldHijackDns(dnsAddr, rAddr.IP, rAddr.Port) {\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer pool.Put(buf)\n\n\t\t\t\t\tmsg, err := relayDns(raw)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)\n\t\t\t\t}()\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tpkt := &packet{\n\t\t\t\tlocal: lAddr,\n\t\t\t\tdata:  raw,\n\t\t\t\twriteBack: func(b []byte, addr net.Addr) (int, error) {\n\t\t\t\t\treturn stack.UDP().WriteTo(b, addr, lAddr)\n\t\t\t\t},\n\t\t\t\tdrop: func() {\n\t\t\t\t\tpool.Put(buf)\n\t\t\t\t},\n\t\t\t}\n\n\t\t\ttunnel.UDPIn() <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.SOCKS5)\n\t\t}\n\t}\n\n\tgo tcp()\n\tgo udp()\n\tgo udp()\n\n\treturn stack, nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tun/udp.go",
    "content": "package tun\n\nimport (\n\t\"net\"\n)\n\ntype packet struct {\n\tlocal     *net.UDPAddr\n\tdata      []byte\n\twriteBack func(b []byte, addr net.Addr) (int, error)\n\tdrop      func()\n}\n\nfunc (pkt *packet) Data() []byte {\n\treturn pkt.data\n}\n\nfunc (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\treturn pkt.writeBack(b, addr)\n}\n\nfunc (pkt *packet) Drop() {\n\tpkt.drop()\n}\n\nfunc (pkt *packet) LocalAddr() net.Addr {\n\treturn pkt.local\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tun.go",
    "content": "package main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"sync\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sync/semaphore\"\n\n\t\"cfa/native/app\"\n\t\"cfa/native/tun\"\n)\n\nvar rTunLock sync.Mutex\nvar rTun *remoteTun\n\ntype remoteTun struct {\n\tcloser   io.Closer\n\tcallback unsafe.Pointer\n\n\tclosed bool\n\tlimit  *semaphore.Weighted\n}\n\nfunc (t *remoteTun) markSocket(fd int) {\n\t_ = t.limit.Acquire(context.Background(), 1)\n\tdefer t.limit.Release(1)\n\n\tif t.closed {\n\t\treturn\n\t}\n\n\tC.mark_socket(t.callback, C.int(fd))\n}\n\nfunc (t *remoteTun) querySocketUid(protocol int, source, target string) int {\n\t_ = t.limit.Acquire(context.Background(), 1)\n\tdefer t.limit.Release(1)\n\n\tif t.closed {\n\t\treturn -1\n\t}\n\n\treturn int(C.query_socket_uid(t.callback, C.int(protocol), C.CString(source), C.CString(target)))\n}\n\nfunc (t *remoteTun) close() {\n\t_ = t.limit.Acquire(context.TODO(), 4)\n\tdefer t.limit.Release(4)\n\n\tt.closed = true\n\n\tif t.closer != nil {\n\t\t_ = t.closer.Close()\n\t}\n\n\tapp.ApplyTunContext(nil, nil)\n\n\tC.release_object(t.callback)\n}\n\n//export startTun\nfunc startTun(fd C.int, gateway, portal, dns C.c_string, callback unsafe.Pointer) C.int {\n\trTunLock.Lock()\n\tdefer rTunLock.Unlock()\n\n\tif rTun != nil {\n\t\trTun.close()\n\t\trTun = nil\n\t}\n\n\tf := int(fd)\n\tg := C.GoString(gateway)\n\tp := C.GoString(portal)\n\td := C.GoString(dns)\n\n\tremote := &remoteTun{callback: callback, closed: false, limit: semaphore.NewWeighted(4)}\n\n\tapp.ApplyTunContext(remote.markSocket, remote.querySocketUid)\n\n\tcloser, err := tun.Start(f, g, p, d)\n\tif err != nil {\n\t\tremote.close()\n\n\t\treturn 1\n\t}\n\n\tremote.closer = closer\n\n\trTun = remote\n\n\treturn 0\n}\n\n//export stopTun\nfunc stopTun() {\n\trTunLock.Lock()\n\tdefer rTunLock.Unlock()\n\n\tif rTun != nil {\n\t\trTun.close()\n\t\trTun = nil\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/conn.go",
    "content": "package tunnel\n\nimport (\n\tC \"github.com/Dreamacro/clash/constant\"\n\t\"github.com/Dreamacro/clash/tunnel/statistic\"\n)\n\nfunc CloseAllConnections() {\n\tfor _, c := range statistic.DefaultManager.Snapshot().Connections {\n\t\t_ = c.Close()\n\t}\n}\n\nfunc closeMatch(filter func(conn C.Conn) bool) {\n\tfor _, c := range statistic.DefaultManager.Snapshot().Connections {\n\t\tif cc, ok := c.(C.Conn); ok {\n\t\t\tif filter(cc) {\n\t\t\t\t_ = cc.Close()\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc closeConnByGroup(name string) {\n\tcloseMatch(func(conn C.Conn) bool {\n\t\tfor _, c := range conn.Chains() {\n\t\t\tif c == name {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t})\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/connectivity.go",
    "content": "package tunnel\n\nimport (\n\t\"sync\"\n\n\t\"github.com/Dreamacro/clash/adapter\"\n\t\"github.com/Dreamacro/clash/adapter/outboundgroup\"\n\t\"github.com/Dreamacro/clash/constant/provider\"\n\t\"github.com/Dreamacro/clash/log\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\nfunc HealthCheck(name string) {\n\tp := tunnel.Proxies()[name]\n\n\tif p == nil {\n\t\tlog.Warnln(\"Request health check for `%s`: not found\", name)\n\n\t\treturn\n\t}\n\n\tg, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)\n\tif !ok {\n\t\tlog.Warnln(\"Request health check for `%s`: invalid type %s\", name, p.Type().String())\n\n\t\treturn\n\t}\n\n\twg := &sync.WaitGroup{}\n\n\tfor _, pr := range g.Providers() {\n\t\twg.Add(1)\n\n\t\tgo func(provider provider.ProxyProvider) {\n\t\t\tprovider.HealthCheck()\n\n\t\t\twg.Done()\n\t\t}(pr)\n\t}\n\n\twg.Wait()\n}\n\nfunc HealthCheckAll() {\n\tfor _, g := range QueryProxyGroupNames(false) {\n\t\tgo func(group string) {\n\t\t\tHealthCheck(group)\n\t\t}(g)\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/geoip.go",
    "content": "package tunnel\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/oschwald/geoip2-golang\"\n\n\t\"github.com/Dreamacro/clash/component/mmdb\"\n)\n\nfunc InstallSideloadGeoip(block []byte) error {\n\tif block == nil {\n\t\tmmdb.InstallOverride(nil)\n\n\t\treturn nil\n\t}\n\n\tdb, err := geoip2.FromBytes(block)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"load sideload geoip mmdb: %s\", err.Error())\n\t}\n\n\tmmdb.InstallOverride(db)\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/init.go",
    "content": "package tunnel\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/Dreamacro/clash/component/dialer\"\n\tC \"github.com/Dreamacro/clash/constant\"\n\tCTX \"github.com/Dreamacro/clash/context\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\nfunc init() {\n\tdialer.DefaultTunnelDialer = func(context context.Context, network, address string) (net.Conn, error) {\n\t\tif !strings.HasPrefix(network, \"tcp\") {\n\t\t\treturn nil, net.UnknownNetworkError(\"unsupported network\")\n\t\t}\n\n\t\thost, port, err := net.SplitHostPort(address)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tleft, right := net.Pipe()\n\n\t\tmetadata := &C.Metadata{\n\t\t\tNetWork:    C.TCP,\n\t\t\tType:       C.HTTPCONNECT,\n\t\t\tSrcIP:      loopback,\n\t\t\tSrcPort:    \"65535\",\n\t\t\tDstPort:    port,\n\t\t\tAddrType:   C.AtypDomainName,\n\t\t\tHost:       host,\n\t\t\tRawSrcAddr: left.RemoteAddr(),\n\t\t\tRawDstAddr: left.LocalAddr(),\n\t\t}\n\n\t\ttunnel.TCPIn() <- CTX.NewConnContext(right, metadata)\n\n\t\treturn left, nil\n\t}\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/loopback_open.go",
    "content": "//go:build !premium\n\npackage tunnel\n\nimport \"net\"\n\nvar loopback = net.ParseIP(\"127.0.0.1\")\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/loopback_premium.go",
    "content": "//go:build premium\n\npackage tunnel\n\nimport \"net/netip\"\n\nvar loopback = netip.MustParseAddr(\"127.0.0.1\")\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/providers_open.go",
    "content": "//go:build !premium\n\npackage tunnel\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\tP \"github.com/Dreamacro/clash/adapter/provider\"\n\t\"github.com/Dreamacro/clash/constant/provider\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\ntype Provider struct {\n\tName        string `json:\"name\"`\n\tVehicleType string `json:\"vehicleType\"`\n\tType        string `json:\"type\"`\n\tUpdatedAt   int64  `json:\"updatedAt\"`\n}\n\nfunc QueryProviders() []*Provider {\n\tp := tunnel.Providers()\n\n\tproviders := make([]provider.Provider, 0, len(p))\n\n\tfor _, proxy := range p {\n\t\tif proxy.VehicleType() == provider.Compatible {\n\t\t\tcontinue\n\t\t}\n\n\t\tproviders = append(providers, proxy)\n\t}\n\n\tresult := make([]*Provider, 0, len(providers))\n\n\tfor _, p := range providers {\n\t\tupdatedAt := time.Time{}\n\n\t\tif s, ok := p.(P.UpdatableProvider); ok {\n\t\t\tupdatedAt = s.UpdatedAt()\n\t\t}\n\n\t\tresult = append(result, &Provider{\n\t\t\tName:        p.Name(),\n\t\t\tVehicleType: p.VehicleType().String(),\n\t\t\tType:        p.Type().String(),\n\t\t\tUpdatedAt:   updatedAt.UnixNano() / 1000 / 1000,\n\t\t})\n\t}\n\n\treturn result\n}\n\nfunc UpdateProvider(_ string, name string) error {\n\tp, ok := tunnel.Providers()[name]\n\tif !ok {\n\t\treturn fmt.Errorf(\"%s not found\", name)\n\t}\n\n\treturn p.Update()\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/providers_premium.go",
    "content": "//go:build premium\n\npackage tunnel\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\tP \"github.com/Dreamacro/clash/adapter/provider\"\n\t\"github.com/Dreamacro/clash/constant/provider\"\n\t\"github.com/Dreamacro/clash/log\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\nvar ErrInvalidType = errors.New(\"invalid type\")\n\ntype Provider struct {\n\tName        string `json:\"name\"`\n\tVehicleType string `json:\"vehicleType\"`\n\tType        string `json:\"type\"`\n\tUpdatedAt   int64  `json:\"updatedAt\"`\n}\n\nfunc QueryProviders() []*Provider {\n\tr := tunnel.RuleProviders()\n\tp := tunnel.ProxyProviders()\n\n\tproviders := make([]provider.Provider, 0, len(r)+len(p))\n\n\tfor _, rule := range r {\n\t\tif rule.VehicleType() == provider.Compatible {\n\t\t\tcontinue\n\t\t}\n\n\t\tproviders = append(providers, rule)\n\t}\n\n\tfor _, proxy := range p {\n\t\tif proxy.VehicleType() == provider.Compatible {\n\t\t\tcontinue\n\t\t}\n\n\t\tproviders = append(providers, proxy)\n\t}\n\n\tresult := make([]*Provider, 0, len(providers))\n\n\tfor _, p := range providers {\n\t\tupdatedAt := time.Time{}\n\n\t\tif s, ok := p.(P.UpdatableProvider[any]); ok {\n\t\t\tupdatedAt = s.UpdatedAt()\n\t\t}\n\n\t\tresult = append(result, &Provider{\n\t\t\tName:        p.Name(),\n\t\t\tVehicleType: p.VehicleType().String(),\n\t\t\tType:        p.Type().String(),\n\t\t\tUpdatedAt:   updatedAt.UnixNano() / 1000 / 1000,\n\t\t})\n\t}\n\n\treturn result\n}\n\nfunc UpdateProvider(t string, name string) error {\n\terr := ErrInvalidType\n\n\tswitch t {\n\tcase \"Rule\":\n\t\tp := tunnel.RuleProviders()[name]\n\t\tif p == nil {\n\t\t\treturn fmt.Errorf(\"%s not found\", name)\n\t\t}\n\n\t\terr = p.Update()\n\tcase \"Proxy\":\n\t\tp := tunnel.ProxyProviders()[name]\n\t\tif p == nil {\n\t\t\treturn fmt.Errorf(\"%s not found\", name)\n\t\t}\n\n\t\terr = p.Update()\n\t}\n\n\tif err != nil {\n\t\tlog.Warnln(\"Updating provider %s: %s\", name, err.Error())\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/proxies.go",
    "content": "package tunnel\n\nimport (\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/dlclark/regexp2\"\n\n\t\"github.com/Dreamacro/clash/adapter\"\n\n\t\"github.com/Dreamacro/clash/adapter/outboundgroup\"\n\tC \"github.com/Dreamacro/clash/constant\"\n\t\"github.com/Dreamacro/clash/constant/provider\"\n\t\"github.com/Dreamacro/clash/log\"\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\ntype SortMode int\n\nconst (\n\tDefault SortMode = iota\n\tTitle\n\tDelay\n)\n\ntype Proxy struct {\n\tName     string `json:\"name\"`\n\tTitle    string `json:\"title\"`\n\tSubtitle string `json:\"subtitle\"`\n\tType     string `json:\"type\"`\n\tDelay    int    `json:\"delay\"`\n}\n\ntype ProxyGroup struct {\n\tType    string   `json:\"type\"`\n\tNow     string   `json:\"now\"`\n\tProxies []*Proxy `json:\"proxies\"`\n}\n\ntype sortableProxyList struct {\n\tlist []*Proxy\n\tless func(a, b *Proxy) bool\n}\n\nfunc (s *sortableProxyList) Len() int {\n\treturn len(s.list)\n}\n\nfunc (s *sortableProxyList) Less(i, j int) bool {\n\treturn s.less(s.list[i], s.list[j])\n}\n\nfunc (s *sortableProxyList) Swap(i, j int) {\n\ts.list[i], s.list[j] = s.list[j], s.list[i]\n}\n\nfunc QueryProxyGroupNames(excludeNotSelectable bool) []string {\n\tmode := tunnel.Mode()\n\n\tif mode == tunnel.Direct {\n\t\treturn []string{}\n\t}\n\n\tglobal := tunnel.Proxies()[\"GLOBAL\"].(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)\n\tproxies := global.Providers()[0].Proxies()\n\tresult := make([]string, 0, len(proxies)+1)\n\n\tif mode == tunnel.Global {\n\t\tresult = append(result, \"GLOBAL\")\n\t}\n\n\tfor _, p := range proxies {\n\t\tif _, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); ok {\n\t\t\tif !excludeNotSelectable || p.Type() == C.Selector {\n\t\t\t\tresult = append(result, p.Name())\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc QueryProxyGroup(name string, sortMode SortMode, uiSubtitlePattern *regexp2.Regexp) *ProxyGroup {\n\tp := tunnel.Proxies()[name]\n\n\tif p == nil {\n\t\tlog.Warnln(\"Query group `%s`: not found\", name)\n\n\t\treturn nil\n\t}\n\n\tg, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)\n\tif !ok {\n\t\tlog.Warnln(\"Query group `%s`: invalid type %s\", name, p.Type().String())\n\n\t\treturn nil\n\t}\n\n\tproxies := collectProviders(g.Providers(), uiSubtitlePattern)\n\n\tswitch sortMode {\n\tcase Title:\n\t\twrapper := &sortableProxyList{\n\t\t\tlist: proxies,\n\t\t\tless: func(a, b *Proxy) bool {\n\t\t\t\treturn strings.Compare(a.Title, b.Title) < 0\n\t\t\t},\n\t\t}\n\n\t\tsort.Sort(wrapper)\n\tcase Delay:\n\t\twrapper := &sortableProxyList{\n\t\t\tlist: proxies,\n\t\t\tless: func(a, b *Proxy) bool {\n\t\t\t\treturn a.Delay < b.Delay\n\t\t\t},\n\t\t}\n\n\t\tsort.Sort(wrapper)\n\tcase Default:\n\tdefault:\n\t}\n\n\treturn &ProxyGroup{\n\t\tType:    g.Type().String(),\n\t\tNow:     g.Now(),\n\t\tProxies: proxies,\n\t}\n}\n\nfunc PatchSelector(selector, name string) bool {\n\tp := tunnel.Proxies()[selector]\n\n\tif p == nil {\n\t\tlog.Warnln(\"Patch selector `%s`: not found\", selector)\n\n\t\treturn false\n\t}\n\n\tg, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)\n\tif !ok {\n\t\tlog.Warnln(\"Patch selector `%s`: invalid type %s\", selector, p.Type().String())\n\n\t\treturn false\n\t}\n\n\ts, ok := g.(*outboundgroup.Selector)\n\tif !ok {\n\t\tlog.Warnln(\"Patch selector `%s`: invalid type %s\", selector, p.Type().String())\n\n\t\treturn false\n\t}\n\n\tif err := s.Set(name); err != nil {\n\t\tlog.Warnln(\"Patch selector `%s`: %s\", selector, err.Error())\n\t}\n\n\tlog.Infoln(\"Patch selector %s -> %s\", selector, name)\n\n\tcloseConnByGroup(selector)\n\n\treturn true\n}\n\nfunc collectProviders(providers []provider.ProxyProvider, uiSubtitlePattern *regexp2.Regexp) []*Proxy {\n\tresult := make([]*Proxy, 0, 128)\n\n\tfor _, p := range providers {\n\t\tfor _, px := range p.Proxies() {\n\t\t\tname := px.Name()\n\t\t\ttitle := name\n\t\t\tsubtitle := px.Type().String()\n\n\t\t\tif uiSubtitlePattern != nil {\n\t\t\t\tif _, ok := px.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); !ok {\n\t\t\t\t\trunes := []rune(name)\n\t\t\t\t\tmatch, err := uiSubtitlePattern.FindRunesMatch(runes)\n\t\t\t\t\tif err == nil && match != nil {\n\t\t\t\t\t\ttitle = string(runes[:match.Index]) + string(runes[match.Index+match.Length:])\n\t\t\t\t\t\tsubtitle = string(runes[match.Index : match.Index+match.Length])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult = append(result, &Proxy{\n\t\t\t\tName:     name,\n\t\t\t\tTitle:    strings.TrimSpace(title),\n\t\t\t\tSubtitle: strings.TrimSpace(subtitle),\n\t\t\t\tType:     px.Type().String(),\n\t\t\t\tDelay:    int(px.LastDelay()),\n\t\t\t})\n\t\t}\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/state.go",
    "content": "package tunnel\n\nimport (\n\t\"github.com/Dreamacro/clash/tunnel\"\n)\n\nfunc QueryMode() string {\n\treturn tunnel.Mode().String()\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/statistic.go",
    "content": "package tunnel\n\nimport (\n\t\"github.com/Dreamacro/clash/tunnel/statistic\"\n)\n\nfunc ResetStatistic() {\n\tstatistic.DefaultManager.ResetStatistic()\n}\n\nfunc Now() (up int64, down int64) {\n\treturn statistic.DefaultManager.Now()\n}\n\nfunc Total() (up int64, down int64) {\n\treturn statistic.DefaultManager.Total()\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel/suspend.go",
    "content": "package tunnel\n\nimport \"github.com/Dreamacro/clash/adapter/provider\"\n\nfunc Suspend(s bool) {\n\tprovider.Suspend(s)\n}\n"
  },
  {
    "path": "core/src/main/golang/native/tunnel.go",
    "content": "package main\n\n//#include \"bridge.h\"\nimport \"C\"\n\nimport (\n\t\"unsafe\"\n\n\t\"cfa/native/app\"\n\t\"cfa/native/tunnel\"\n)\n\n//export queryTunnelState\nfunc queryTunnelState() *C.char {\n\tmode := tunnel.QueryMode()\n\n\tresponse := &struct {\n\t\tMode string `json:\"mode\"`\n\t}{mode}\n\n\treturn marshalJson(response)\n}\n\n//export queryNow\nfunc queryNow(upload, download *C.uint64_t) {\n\tup, down := tunnel.Now()\n\n\t*upload = C.uint64_t(up)\n\t*download = C.uint64_t(down)\n}\n\n//export queryTotal\nfunc queryTotal(upload, download *C.uint64_t) {\n\tup, down := tunnel.Total()\n\n\t*upload = C.uint64_t(up)\n\t*download = C.uint64_t(down)\n}\n\n//export queryGroupNames\nfunc queryGroupNames(excludeNotSelectable C.int) *C.char {\n\treturn marshalJson(tunnel.QueryProxyGroupNames(excludeNotSelectable != 0))\n}\n\n//export queryGroup\nfunc queryGroup(name C.c_string, sortMode C.c_string) *C.char {\n\tn := C.GoString(name)\n\ts := C.GoString(sortMode)\n\n\tmode := tunnel.Default\n\n\tswitch s {\n\tcase \"Title\":\n\t\tmode = tunnel.Title\n\tcase \"Delay\":\n\t\tmode = tunnel.Delay\n\t}\n\n\tresponse := tunnel.QueryProxyGroup(n, mode, app.SubtitlePattern())\n\n\tif response == nil {\n\t\treturn nil\n\t}\n\n\treturn marshalJson(response)\n}\n\n//export healthCheck\nfunc healthCheck(completable unsafe.Pointer, name C.c_string) {\n\tgo func(name string) {\n\t\ttunnel.HealthCheck(name)\n\n\t\tC.complete(completable, nil)\n\t}(C.GoString(name))\n}\n\n//export healthCheckAll\nfunc healthCheckAll() {\n\ttunnel.HealthCheckAll()\n}\n\n//export patchSelector\nfunc patchSelector(selector, name C.c_string) C.int {\n\ts := C.GoString(selector)\n\tn := C.GoString(name)\n\n\tif tunnel.PatchSelector(s, n) {\n\t\treturn 1\n\t}\n\n\treturn 0\n}\n\n//export queryProviders\nfunc queryProviders() *C.char {\n\treturn marshalJson(tunnel.QueryProviders())\n}\n\n//export updateProvider\nfunc updateProvider(completable unsafe.Pointer, pType C.c_string, name C.c_string) {\n\tgo func(pType, name string) {\n\t\tC.complete(completable, marshalString(tunnel.UpdateProvider(pType, name)))\n\n\t\tC.release_object(completable)\n\t}(C.GoString(pType), C.GoString(name))\n}\n\n//export suspend\nfunc suspend(suspended C.int) {\n\ttunnel.Suspend(suspended != 0)\n}\n\n//export installSideloadGeoip\nfunc installSideloadGeoip(block unsafe.Pointer, blockSize C.int) *C.char {\n\tif block == nil {\n\t\t_ = tunnel.InstallSideloadGeoip(nil)\n\n\t\treturn nil\n\t}\n\n\tbytes := C.GoBytes(block, blockSize)\n\n\treturn marshalString(tunnel.InstallSideloadGeoip(bytes))\n}\n"
  },
  {
    "path": "core/src/main/golang/native/utils.go",
    "content": "package main\n\nimport \"C\"\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n)\n\nfunc marshalJson(obj any) *C.char {\n\tres, err := json.Marshal(obj)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treturn C.CString(string(res))\n}\n\nfunc marshalString(obj any) *C.char {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\n\tswitch o := obj.(type) {\n\tcase error:\n\t\treturn C.CString(o.Error())\n\tcase string:\n\t\treturn C.CString(o)\n\t}\n\n\tpanic(\"invalid marshal type \" + reflect.TypeOf(obj).Name())\n}\n"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/Clash.kt",
    "content": "package com.github.kr328.clash.core\n\nimport com.github.kr328.clash.core.bridge.Bridge\nimport com.github.kr328.clash.core.bridge.ClashException\nimport com.github.kr328.clash.core.bridge.FetchCallback\nimport com.github.kr328.clash.core.bridge.LogcatInterface\nimport com.github.kr328.clash.core.bridge.TunInterface\nimport com.github.kr328.clash.core.model.ConfigurationOverride\nimport com.github.kr328.clash.core.model.FetchStatus\nimport com.github.kr328.clash.core.model.LogMessage\nimport com.github.kr328.clash.core.model.Provider\nimport com.github.kr328.clash.core.model.Proxy\nimport com.github.kr328.clash.core.model.ProxyGroup\nimport com.github.kr328.clash.core.model.ProxySort\nimport com.github.kr328.clash.core.model.Traffic\nimport com.github.kr328.clash.core.model.TunnelState\nimport com.github.kr328.clash.core.model.UiConfiguration\nimport com.github.kr328.clash.core.util.parseInetSocketAddress\nimport kotlinx.coroutines.CompletableDeferred\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.channels.ReceiveChannel\nimport kotlinx.serialization.json.Json\nimport kotlinx.serialization.json.JsonArray\nimport kotlinx.serialization.json.jsonPrimitive\nimport java.io.File\nimport java.net.InetSocketAddress\n\nobject Clash {\n    enum class OverrideSlot {\n        Persist, Session\n    }\n\n    private val ConfigurationOverrideJson = Json {\n        ignoreUnknownKeys = true\n        encodeDefaults = false\n    }\n\n    fun reset() {\n        Bridge.nativeReset()\n    }\n\n    fun forceGc() {\n        Bridge.nativeForceGc()\n    }\n\n    fun suspendCore(suspended: Boolean) {\n        Bridge.nativeSuspend(suspended)\n    }\n\n    fun queryTunnelState(): TunnelState {\n        val json = Bridge.nativeQueryTunnelState()\n\n        return Json.decodeFromString(TunnelState.serializer(), json)\n    }\n\n    fun queryTrafficNow(): Traffic {\n        return Bridge.nativeQueryTrafficNow()\n    }\n\n    fun queryTrafficTotal(): Traffic {\n        return Bridge.nativeQueryTrafficTotal()\n    }\n\n    fun notifyDnsChanged(dns: List<String>) {\n        Bridge.nativeNotifyDnsChanged(dns.joinToString(separator = \",\"))\n    }\n\n    fun notifyTimeZoneChanged(name: String, offset: Int) {\n        Bridge.nativeNotifyTimeZoneChanged(name, offset)\n    }\n\n    fun notifyInstalledAppsChanged(uids: List<Pair<Int, String>>) {\n        val uidList = uids.joinToString(separator = \",\") { \"${it.first}:${it.second}\" }\n\n        Bridge.nativeNotifyInstalledAppChanged(uidList)\n    }\n\n    fun startTun(\n        fd: Int,\n        gateway: String,\n        portal: String,\n        dns: String,\n        markSocket: (Int) -> Boolean,\n        querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int\n    ) {\n        Bridge.nativeStartTun(fd, gateway, portal, dns, object : TunInterface {\n            override fun markSocket(fd: Int) {\n                markSocket(fd)\n            }\n\n            override fun querySocketUid(protocol: Int, source: String, target: String): Int {\n                return querySocketUid(\n                    protocol,\n                    parseInetSocketAddress(source),\n                    parseInetSocketAddress(target)\n                )\n            }\n        })\n    }\n\n    fun stopTun() {\n        Bridge.nativeStopTun()\n    }\n\n    fun startHttp(listenAt: String): String? {\n        return Bridge.nativeStartHttp(listenAt)\n    }\n\n    fun stopHttp() {\n        Bridge.nativeStopHttp()\n    }\n\n    fun queryGroupNames(excludeNotSelectable: Boolean): List<String> {\n        val names = Json.Default.decodeFromString(\n            JsonArray.serializer(),\n            Bridge.nativeQueryGroupNames(excludeNotSelectable)\n        )\n\n        return names.map {\n            require(it.jsonPrimitive.isString)\n\n            it.jsonPrimitive.content\n        }\n    }\n\n    fun queryGroup(name: String, sort: ProxySort): ProxyGroup {\n        return Bridge.nativeQueryGroup(name, sort.name)\n            ?.let { Json.Default.decodeFromString(ProxyGroup.serializer(), it) }\n            ?: ProxyGroup(Proxy.Type.Unknown, emptyList(), \"\")\n    }\n\n    fun healthCheck(name: String): CompletableDeferred<Unit> {\n        return CompletableDeferred<Unit>().apply {\n            Bridge.nativeHealthCheck(this, name)\n        }\n    }\n\n    fun healthCheckAll() {\n        Bridge.nativeHealthCheckAll()\n    }\n\n    fun patchSelector(selector: String, name: String): Boolean {\n        return Bridge.nativePatchSelector(selector, name)\n    }\n\n    fun fetchAndValid(\n        path: File,\n        url: String,\n        force: Boolean,\n        reportStatus: (FetchStatus) -> Unit\n    ): CompletableDeferred<Unit> {\n        return CompletableDeferred<Unit>().apply {\n            Bridge.nativeFetchAndValid(\n                object : FetchCallback {\n                    override fun report(statusJson: String) {\n                        reportStatus(\n                            Json.Default.decodeFromString(\n                                FetchStatus.serializer(),\n                                statusJson\n                            )\n                        )\n                    }\n\n                    override fun complete(error: String?) {\n                        if (error != null)\n                            completeExceptionally(ClashException(error))\n                        else\n                            complete(Unit)\n                    }\n                },\n                path.absolutePath,\n                url,\n                force\n            )\n        }\n    }\n\n    fun load(path: File): CompletableDeferred<Unit> {\n        return CompletableDeferred<Unit>().apply {\n            Bridge.nativeLoad(this, path.absolutePath)\n        }\n    }\n\n    fun queryProviders(): List<Provider> {\n        val providers =\n            Json.Default.decodeFromString(JsonArray.serializer(), Bridge.nativeQueryProviders())\n\n        return List(providers.size) {\n            Json.Default.decodeFromJsonElement(Provider.serializer(), providers[it])\n        }\n    }\n\n    fun updateProvider(type: Provider.Type, name: String): CompletableDeferred<Unit> {\n        return CompletableDeferred<Unit>().apply {\n            Bridge.nativeUpdateProvider(this, type.toString(), name)\n        }\n    }\n\n    fun queryOverride(slot: OverrideSlot): ConfigurationOverride {\n        return try {\n            ConfigurationOverrideJson.decodeFromString(\n                ConfigurationOverride.serializer(),\n                Bridge.nativeReadOverride(slot.ordinal)\n            )\n        } catch (e: Exception) {\n            ConfigurationOverride()\n        }\n    }\n\n    fun patchOverride(slot: OverrideSlot, configuration: ConfigurationOverride) {\n        Bridge.nativeWriteOverride(\n            slot.ordinal,\n            ConfigurationOverrideJson.encodeToString(\n                ConfigurationOverride.serializer(),\n                configuration\n            )\n        )\n    }\n\n    fun clearOverride(slot: OverrideSlot) {\n        Bridge.nativeClearOverride(slot.ordinal)\n    }\n\n    fun installSideloadGeoip(data: ByteArray?) {\n        Bridge.nativeInstallSideloadGeoip(data)\n    }\n\n    fun queryConfiguration(): UiConfiguration {\n        return Json.Default.decodeFromString(\n            UiConfiguration.serializer(),\n            Bridge.nativeQueryConfiguration()\n        )\n    }\n\n    fun subscribeLogcat(): ReceiveChannel<LogMessage> {\n        return Channel<LogMessage>(32).apply {\n            Bridge.nativeSubscribeLogcat(object : LogcatInterface {\n                override fun received(jsonPayload: String) {\n                    trySend(Json.decodeFromString(LogMessage.serializer(), jsonPayload))\n                }\n            })\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/bridge/Bridge.kt",
    "content": "package com.github.kr328.clash.core.bridge\n\nimport android.os.Build\nimport android.os.ParcelFileDescriptor\nimport androidx.annotation.Keep\nimport yos.clash.material.common.Global\nimport yos.clash.material.common.log.Log\nimport kotlinx.coroutines.CompletableDeferred\nimport java.io.File\n\n@Keep\nobject Bridge {\n    external fun nativeReset()\n    external fun nativeForceGc()\n    external fun nativeSuspend(suspend: Boolean)\n    external fun nativeQueryTunnelState(): String\n    external fun nativeQueryTrafficNow(): Long\n    external fun nativeQueryTrafficTotal(): Long\n    external fun nativeNotifyDnsChanged(dnsList: String)\n    external fun nativeNotifyTimeZoneChanged(name: String, offset: Int)\n    external fun nativeNotifyInstalledAppChanged(uidList: String)\n    external fun nativeStartTun(fd: Int, gateway: String, portal: String, dns: String, cb: TunInterface)\n    external fun nativeStopTun()\n    external fun nativeStartHttp(listenAt: String): String?\n    external fun nativeStopHttp()\n    external fun nativeQueryGroupNames(excludeNotSelectable: Boolean): String\n    external fun nativeQueryGroup(name: String, sort: String): String?\n    external fun nativeHealthCheck(completable: CompletableDeferred<Unit>, name: String)\n    external fun nativeHealthCheckAll()\n    external fun nativePatchSelector(selector: String, name: String): Boolean\n    external fun nativeFetchAndValid(\n        completable: FetchCallback,\n        path: String,\n        url: String,\n        force: Boolean\n    )\n\n    external fun nativeLoad(completable: CompletableDeferred<Unit>, path: String)\n    external fun nativeQueryProviders(): String\n    external fun nativeUpdateProvider(\n        completable: CompletableDeferred<Unit>,\n        type: String,\n        name: String\n    )\n\n    external fun nativeReadOverride(slot: Int): String\n    external fun nativeWriteOverride(slot: Int, content: String)\n    external fun nativeClearOverride(slot: Int)\n    external fun nativeInstallSideloadGeoip(data: ByteArray?)\n    external fun nativeQueryConfiguration(): String\n    external fun nativeSubscribeLogcat(callback: LogcatInterface)\n\n    private external fun nativeInit(home: String, versionName: String, sdkVersion: Int)\n\n    init {\n        System.loadLibrary(\"bridge\")\n\n        val ctx = Global.application\n\n        ParcelFileDescriptor.open(File(ctx.packageCodePath), ParcelFileDescriptor.MODE_READ_ONLY)\n            .detachFd()\n\n        val home = ctx.filesDir.resolve(\"clash\").apply { mkdirs() }.absolutePath\n        val versionName = ctx.packageManager.getPackageInfo(ctx.packageName, 0).versionName\n        val sdkVersion = Build.VERSION.SDK_INT\n\n        Log.d(\"Home = $home\")\n\n        nativeInit(home, versionName, sdkVersion)\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/bridge/ClashException.kt",
    "content": "package com.github.kr328.clash.core.bridge\n\nimport androidx.annotation.Keep\n\n@Keep\nclass ClashException(msg: String) : IllegalArgumentException(msg)"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/bridge/Content.kt",
    "content": "package com.github.kr328.clash.core.bridge\n\nimport android.net.Uri\nimport androidx.annotation.Keep\nimport yos.clash.material.common.Global\nimport java.io.FileNotFoundException\n\n@Keep\nobject Content {\n    @JvmStatic\n    fun open(url: String): Int {\n        val uri = Uri.parse(url)\n\n        if (uri.scheme != \"content\") {\n            throw UnsupportedOperationException(\"Unsupported scheme ${uri.scheme}\")\n        }\n\n        return Global.application.contentResolver.openFileDescriptor(uri, \"r\")?.detachFd()\n            ?: throw FileNotFoundException(\"$uri not found\")\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/bridge/FetchCallback.kt",
    "content": "package com.github.kr328.clash.core.bridge\n\nimport androidx.annotation.Keep\n\n@Keep\ninterface FetchCallback {\n    fun report(statusJson: String)\n    fun complete(error: String?)\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/bridge/LogcatInterface.kt",
    "content": "package com.github.kr328.clash.core.bridge\n\nimport androidx.annotation.Keep\n\n@Keep\ninterface LogcatInterface {\n    fun received(jsonPayload: String)\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/bridge/TunInterface.kt",
    "content": "package com.github.kr328.clash.core.bridge\n\nimport androidx.annotation.Keep\n\n@Keep\ninterface TunInterface {\n    fun markSocket(fd: Int)\n    fun querySocketUid(protocol: Int, source: String, target: String): Int\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/ConfigurationOverride.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class ConfigurationOverride(\n    @SerialName(\"port\")\n    var httpPort: Int? = null,\n\n    @SerialName(\"socks-port\")\n    var socksPort: Int? = null,\n\n    @SerialName(\"redir-port\")\n    var redirectPort: Int? = null,\n\n    @SerialName(\"tproxy-port\")\n    var tproxyPort: Int? = null,\n\n    @SerialName(\"mixed-port\")\n    var mixedPort: Int? = null,\n\n    @SerialName(\"authentication\")\n    var authentication: List<String>? = null,\n\n    @SerialName(\"allow-lan\")\n    var allowLan: Boolean? = null,\n\n    @SerialName(\"bind-address\")\n    var bindAddress: String? = null,\n\n    @SerialName(\"mode\")\n    var mode: TunnelState.Mode? = null,\n\n    @SerialName(\"log-level\")\n    var logLevel: LogMessage.Level? = null,\n\n    @SerialName(\"ipv6\")\n    var ipv6: Boolean? = null,\n\n    @SerialName(\"hosts\")\n    var hosts: Map<String, String>? = null,\n\n    @SerialName(\"dns\")\n    val dns: Dns = Dns(),\n\n    @SerialName(\"clash-for-android\")\n    val app: App = App(),\n\n    @SerialName(\"experimental\")\n    val experimental: Experimental = Experimental()\n) : Parcelable {\n    @Serializable\n    data class Dns(\n        @SerialName(\"enable\")\n        var enable: Boolean? = null,\n\n        @SerialName(\"listen\")\n        var listen: String? = null,\n\n        @SerialName(\"ipv6\")\n        var ipv6: Boolean? = null,\n\n        @SerialName(\"use-hosts\")\n        var useHosts: Boolean? = null,\n\n        @SerialName(\"enhanced-mode\")\n        var enhancedMode: DnsEnhancedMode? = null,\n\n        @SerialName(\"nameserver\")\n        var nameServer: List<String>? = null,\n\n        @SerialName(\"fallback\")\n        var fallback: List<String>? = null,\n\n        @SerialName(\"default-nameserver\")\n        var defaultServer: List<String>? = null,\n\n        @SerialName(\"fake-ip-filter\")\n        var fakeIpFilter: List<String>? = null,\n\n        @SerialName(\"fallback-filter\")\n        val fallbackFilter: DnsFallbackFilter = DnsFallbackFilter(),\n\n        @SerialName(\"nameserver-policy\")\n        var nameserverPolicy: Map<String, String>? = null,\n    )\n\n    @Serializable\n    data class DnsFallbackFilter(\n        @SerialName(\"geoip\")\n        var geoIp: Boolean? = null,\n\n        @SerialName(\"geoip-code\")\n        var geoIpCode: String? = null,\n\n        @SerialName(\"ipcidr\")\n        var ipcidr: List<String>? = null,\n\n        @SerialName(\"domain\")\n        var domain: List<String>? = null,\n    )\n\n    @Serializable\n    data class App(\n        @SerialName(\"append-system-dns\")\n        var appendSystemDns: Boolean? = null\n    )\n\n    @Serializable\n    data class Experimental(\n        @SerialName(\"sniff-tls-sni\")\n        var sniffTLSSNI: Boolean? = null,\n    )\n\n    @Serializable\n    enum class DnsEnhancedMode {\n        @SerialName(\"normal\")\n        None,\n\n        @SerialName(\"redir-host\")\n        Mapping,\n\n        @SerialName(\"fake-ip\")\n        FakeIp,\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object CREATOR : Parcelable.Creator<ConfigurationOverride> {\n        override fun createFromParcel(parcel: Parcel): ConfigurationOverride {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<ConfigurationOverride?> {\n            return arrayOfNulls(size)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/FetchStatus.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class FetchStatus(\n    val action: Action,\n    val args: List<String>,\n    val progress: Int,\n    val max: Int\n) : Parcelable {\n    enum class Action {\n        FetchConfiguration,\n        FetchProviders,\n        Verifying,\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), dest, this)\n    }\n\n    companion object CREATOR : Parcelable.Creator<FetchStatus> {\n        override fun createFromParcel(parcel: Parcel): FetchStatus {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<FetchStatus?> {\n            return arrayOfNulls(size)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/LogMessage.kt",
    "content": "@file:UseSerializers(DateSerializer::class)\n\npackage com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.DateSerializer\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.UseSerializers\nimport java.util.*\n\n@Serializable\ndata class LogMessage(\n    val level: Level,\n    val message: String,\n    val time: Date,\n) : Parcelable {\n    @Serializable\n    enum class Level {\n        @SerialName(\"debug\")\n        Debug,\n        @SerialName(\"info\")\n        Info,\n        @SerialName(\"warning\")\n        Warning,\n        @SerialName(\"error\")\n        Error,\n        @SerialName(\"silent\")\n        Silent,\n        @SerialName(\"unknown\")\n        Unknown,\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<LogMessage> {\n            override fun createFromParcel(parcel: Parcel): LogMessage {\n                return Parcelizer.decodeFromParcel(serializer(), parcel)\n            }\n\n            override fun newArray(size: Int): Array<LogMessage?> {\n                return arrayOfNulls(size)\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/Provider.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class Provider(\n    val name: String,\n    val type: Type,\n    val vehicleType: VehicleType,\n    val updatedAt: Long\n) : Parcelable, Comparable<Provider> {\n    enum class Type {\n        Proxy, Rule\n    }\n\n    enum class VehicleType {\n        HTTP, File, Compatible\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    override fun compareTo(other: Provider): Int {\n        return compareValuesBy(this, other, Provider::type, Provider::name)\n    }\n\n    companion object CREATOR : Parcelable.Creator<Provider> {\n        override fun createFromParcel(parcel: Parcel): Provider {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<Provider?> {\n            return arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/ProviderList.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport yos.clash.material.common.util.createListFromParcelSlice\nimport yos.clash.material.common.util.writeToParcelSlice\n\nclass ProviderList(data: List<Provider>) : List<Provider> by data, Parcelable {\n    constructor(parcel: Parcel) : this(Provider.createListFromParcelSlice(parcel, 0, 20))\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        return writeToParcelSlice(parcel, flags)\n    }\n\n    companion object CREATOR : Parcelable.Creator<ProviderList> {\n        override fun createFromParcel(parcel: Parcel): ProviderList {\n            return ProviderList(parcel)\n        }\n\n        override fun newArray(size: Int): Array<ProviderList?> {\n            return arrayOfNulls(size)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/Proxy.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class Proxy(\n    val name: String,\n    val title: String,\n    val subtitle: String,\n    val type: Type,\n    val delay: Int,\n) : Parcelable {\n    @Suppress(\"unused\")\n    enum class Type(val group: Boolean) {\n        Direct(false),\n        Reject(false),\n\n        Shadowsocks(false),\n        ShadowsocksR(false),\n        Snell(false),\n        Socks5(false),\n        Http(false),\n        Vmess(false),\n        Trojan(false),\n\n        Relay(true),\n        Selector(true),\n        Fallback(true),\n        URLTest(true),\n        LoadBalance(true),\n\n        Unknown(false);\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object CREATOR : Parcelable.Creator<Proxy> {\n        override fun createFromParcel(parcel: Parcel): Proxy {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<Proxy?> {\n            return arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/ProxyGroup.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport yos.clash.material.common.util.createListFromParcelSlice\nimport yos.clash.material.common.util.writeToParcelSlice\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class ProxyGroup(\n    val type: Proxy.Type,\n    val proxies: List<Proxy>,\n    val now: String,\n) : Parcelable {\n    class SliceProxyList(data: List<Proxy>) : List<Proxy> by data, Parcelable {\n        constructor(parcel: Parcel) : this(Proxy.createListFromParcelSlice(parcel, 0, 50))\n\n        override fun describeContents(): Int {\n            return 0\n        }\n\n        override fun writeToParcel(dest: Parcel, flags: Int) {\n            writeToParcelSlice(dest, flags)\n        }\n\n        companion object CREATOR : Parcelable.Creator<SliceProxyList> {\n            override fun createFromParcel(parcel: Parcel): SliceProxyList {\n                return SliceProxyList(parcel)\n            }\n\n            override fun newArray(size: Int): Array<SliceProxyList?> {\n                return arrayOfNulls(size)\n            }\n        }\n    }\n\n    constructor(parcel: Parcel) : this(\n        Proxy.Type.values()[parcel.readInt()],\n        SliceProxyList(parcel),\n        parcel.readString()!!,\n    )\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        parcel.writeInt(type.ordinal)\n        SliceProxyList(proxies).writeToParcel(parcel, 0)\n        parcel.writeString(now)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object CREATOR : Parcelable.Creator<ProxyGroup> {\n        override fun createFromParcel(parcel: Parcel): ProxyGroup {\n            return ProxyGroup(parcel)\n        }\n\n        override fun newArray(size: Int): Array<ProxyGroup?> {\n            return arrayOfNulls(size)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/ProxySort.kt",
    "content": "package com.github.kr328.clash.core.model\n\nenum class ProxySort {\n    Default, Title, Delay\n}\n"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/Traffic.kt",
    "content": "package com.github.kr328.clash.core.model\n\ntypealias Traffic = Long"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/TunnelState.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class TunnelState(\n    val mode: Mode,\n) : Parcelable {\n    @Serializable\n    enum class Mode {\n        @SerialName(\"direct\")\n        Direct,\n\n        @SerialName(\"global\")\n        Global,\n\n        @SerialName(\"rule\")\n        Rule,\n\n        @SerialName(\"script\")\n        Script,\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object CREATOR : Parcelable.Creator<TunnelState> {\n        override fun createFromParcel(parcel: Parcel): TunnelState {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<TunnelState?> {\n            return arrayOfNulls(size)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/model/UiConfiguration.kt",
    "content": "package com.github.kr328.clash.core.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport kotlinx.serialization.Serializable\n\n@Serializable\nclass UiConfiguration : Parcelable {\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object CREATOR : Parcelable.Creator<UiConfiguration> {\n        override fun createFromParcel(parcel: Parcel): UiConfiguration {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<UiConfiguration?> {\n            return arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/util/Net.kt",
    "content": "package com.github.kr328.clash.core.util\n\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.net.URL\n\nfun parseInetSocketAddress(address: String): InetSocketAddress {\n    val url = URL(\"https://$address\")\n\n    return InetSocketAddress(InetAddress.getByName(url.host), url.port)\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/util/Parcelizer.kt",
    "content": "package com.github.kr328.clash.core.util\n\nimport android.os.Parcel\nimport kotlinx.serialization.DeserializationStrategy\nimport kotlinx.serialization.ExperimentalSerializationApi\nimport kotlinx.serialization.SerializationStrategy\nimport kotlinx.serialization.descriptors.SerialDescriptor\nimport kotlinx.serialization.encoding.CompositeDecoder\nimport kotlinx.serialization.encoding.CompositeEncoder\nimport kotlinx.serialization.encoding.Decoder\nimport kotlinx.serialization.encoding.Encoder\nimport kotlinx.serialization.modules.SerializersModule\n\nobject Parcelizer {\n    private class ParcelDecoder(private val parcel: Parcel) : Decoder, CompositeDecoder {\n        override val serializersModule: SerializersModule = SerializersModule {}\n\n        @ExperimentalSerializationApi\n        override fun decodeSequentially(): Boolean = true\n        override fun decodeByteElement(descriptor: SerialDescriptor, index: Int) = decodeByte()\n        override fun decodeCharElement(descriptor: SerialDescriptor, index: Int) = decodeChar()\n        override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int) = decodeDouble()\n        override fun decodeElementIndex(descriptor: SerialDescriptor) = decodeInt()\n        override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int) = decodeFloat()\n        override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int) =\n            decodeBoolean()\n\n        @ExperimentalSerializationApi\n        override fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder {\n            return this\n        }\n\n        override fun decodeIntElement(descriptor: SerialDescriptor, index: Int) = decodeInt()\n        override fun decodeLongElement(descriptor: SerialDescriptor, index: Int) = decodeLong()\n        override fun decodeShortElement(descriptor: SerialDescriptor, index: Int) = decodeShort()\n        override fun decodeStringElement(descriptor: SerialDescriptor, index: Int) = decodeString()\n\n        @ExperimentalSerializationApi\n        override fun <T : Any> decodeNullableSerializableElement(\n            descriptor: SerialDescriptor,\n            index: Int,\n            deserializer: DeserializationStrategy<T?>,\n            previousValue: T?\n        ): T? = decodeNullableSerializableValue(deserializer)\n\n        override fun <T> decodeSerializableElement(\n            descriptor: SerialDescriptor,\n            index: Int,\n            deserializer: DeserializationStrategy<T>,\n            previousValue: T?\n        ): T = decodeSerializableValue(deserializer)\n\n\n        override fun endStructure(descriptor: SerialDescriptor) {\n\n        }\n\n        override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {\n            return this\n        }\n\n        override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {\n            return decodeInt()\n        }\n\n        override fun decodeBoolean(): Boolean {\n            return decodeByte() != (0.toByte())\n        }\n\n        override fun decodeByte(): Byte {\n            return parcel.readByte()\n        }\n\n        override fun decodeChar(): Char {\n            return decodeInt().toChar()\n        }\n\n        override fun decodeDouble(): Double {\n            return parcel.readDouble()\n        }\n\n        override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {\n            return decodeInt()\n        }\n\n        override fun decodeFloat(): Float {\n            return parcel.readFloat()\n        }\n\n        @ExperimentalSerializationApi\n        override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder {\n            return this\n        }\n\n        override fun decodeInt(): Int {\n            return parcel.readInt()\n        }\n\n        override fun decodeLong(): Long {\n            return parcel.readLong()\n        }\n\n        @ExperimentalSerializationApi\n        override fun decodeNotNullMark(): Boolean {\n            return decodeBoolean()\n        }\n\n        @ExperimentalSerializationApi\n        override fun decodeNull(): Nothing? {\n            return null\n        }\n\n        override fun decodeShort(): Short {\n            return decodeInt().toShort()\n        }\n\n        override fun decodeString(): String {\n            return parcel.readString()!!\n        }\n    }\n\n    private class ParcelEncoder(private val parcel: Parcel) : Encoder, CompositeEncoder {\n        override val serializersModule: SerializersModule = SerializersModule {}\n\n        override fun encodeBooleanElement(\n            descriptor: SerialDescriptor,\n            index: Int,\n            value: Boolean\n        ) = encodeBoolean(value)\n\n        override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) =\n            encodeByte(value)\n\n        override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) =\n            encodeChar(value)\n\n        override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) =\n            encodeDouble(value)\n\n        override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float) =\n            encodeFloat(value)\n\n        @ExperimentalSerializationApi\n        override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder {\n            return this\n        }\n\n        override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int) =\n            encodeInt(value)\n\n        override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long) =\n            encodeLong(value)\n\n        override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short) =\n            encodeShort(value)\n\n        override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String) =\n            encodeString(value)\n\n        @ExperimentalSerializationApi\n        override fun <T : Any> encodeNullableSerializableElement(\n            descriptor: SerialDescriptor,\n            index: Int,\n            serializer: SerializationStrategy<T>,\n            value: T?\n        ) = encodeNullableSerializableValue(serializer, value)\n\n        override fun <T> encodeSerializableElement(\n            descriptor: SerialDescriptor,\n            index: Int,\n            serializer: SerializationStrategy<T>,\n            value: T\n        ) = encodeSerializableValue(serializer, value)\n\n        override fun endStructure(descriptor: SerialDescriptor) {\n\n        }\n\n        override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {\n            return this\n        }\n\n        override fun beginCollection(\n            descriptor: SerialDescriptor,\n            collectionSize: Int\n        ): CompositeEncoder {\n            encodeInt(collectionSize)\n\n            return super.beginCollection(descriptor, collectionSize)\n        }\n\n        override fun encodeBoolean(value: Boolean) {\n            encodeByte(if (value) 1 else 0)\n        }\n\n        override fun encodeByte(value: Byte) {\n            parcel.writeByte(value)\n        }\n\n        override fun encodeChar(value: Char) {\n            encodeInt(value.code)\n        }\n\n        override fun encodeDouble(value: Double) {\n            parcel.writeDouble(value)\n        }\n\n        override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {\n            encodeInt(index)\n        }\n\n        override fun encodeFloat(value: Float) {\n            parcel.writeFloat(value)\n        }\n\n        @ExperimentalSerializationApi\n        override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder {\n            return this\n        }\n\n        override fun encodeInt(value: Int) {\n            parcel.writeInt(value)\n        }\n\n        override fun encodeLong(value: Long) {\n            parcel.writeLong(value)\n        }\n\n        @ExperimentalSerializationApi\n        override fun encodeNull() {\n            encodeBoolean(false)\n        }\n\n        override fun encodeShort(value: Short) {\n            encodeInt(value.toInt())\n        }\n\n        override fun encodeString(value: String) {\n            parcel.writeString(value)\n        }\n\n        @ExperimentalSerializationApi\n        override fun encodeNotNullMark() {\n            encodeBoolean(true)\n        }\n    }\n\n    fun <T> decodeFromParcel(deserializer: DeserializationStrategy<T>, parcel: Parcel): T {\n        return deserializer.deserialize(ParcelDecoder(parcel))\n    }\n\n    fun <T> encodeToParcel(serializer: SerializationStrategy<T>, parcel: Parcel, value: T) {\n        serializer.serialize(ParcelEncoder(parcel), value)\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/util/Serializers.kt",
    "content": "package com.github.kr328.clash.core.util\n\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.descriptors.PrimitiveKind\nimport kotlinx.serialization.descriptors.PrimitiveSerialDescriptor\nimport kotlinx.serialization.descriptors.SerialDescriptor\nimport kotlinx.serialization.encoding.Decoder\nimport kotlinx.serialization.encoding.Encoder\nimport java.util.*\n\nobject DateSerializer : KSerializer<Date> {\n    override val descriptor: SerialDescriptor\n        get() = PrimitiveSerialDescriptor(\"Date\", PrimitiveKind.LONG)\n\n    override fun deserialize(decoder: Decoder): Date {\n        return Date(decoder.decodeLong())\n    }\n\n    override fun serialize(encoder: Encoder, value: Date) {\n        encoder.encodeLong(value.time)\n    }\n}"
  },
  {
    "path": "core/src/main/java/com/github/kr328/clash/core/util/Traffic.kt",
    "content": "package com.github.kr328.clash.core.util\n\nimport com.github.kr328.clash.core.model.Traffic\n\nfun Traffic.trafficUpload(): String {\n    return trafficString(scaleTraffic(this ushr 32))\n}\n\nfun Traffic.trafficDownload(): String {\n    return trafficString(scaleTraffic(this and 0xFFFFFFFF))\n}\n\nfun Traffic.trafficTotal(): String {\n    val upload = scaleTraffic(this ushr 32)\n    val download = scaleTraffic(this and 0xFFFFFFFF)\n\n    return trafficString(upload + download)\n}\n\nprivate fun trafficString(scaled: Long): String {\n    return when {\n        scaled > 1024 * 1024 * 1024 * 100L -> {\n            val data = scaled / 1024 / 1024 / 1024\n\n            String.format(\"%.2f GiB\", data.toFloat() / 100)\n        }\n        scaled > 1024 * 1024 * 100L -> {\n            val data = scaled / 1024 / 1024\n\n            String.format(\"%.2f MiB\", data.toFloat() / 100)\n        }\n        scaled > 1024 * 100L -> {\n            val data = scaled / 1024\n\n            String.format(\"%.2f KiB\", data.toFloat() / 100)\n        }\n        else -> {\n            \"$scaled Bytes\"\n        }\n    }\n}\n\nprivate fun scaleTraffic(value: Long): Long {\n    val type = (value ushr 30) and 0x3\n    val data = value and 0x3FFFFFFF\n\n    return when (type) {\n        0L -> data\n        1L -> data * 1024\n        2L -> data * 1024 * 1024\n        3L -> data * 1024 * 1024 * 1024\n        else -> throw IllegalArgumentException(\"invalid value type\")\n    }\n}"
  },
  {
    "path": "core/src/premium/golang/go.mod",
    "content": "module premium\n\ngo 1.18\n\nrequire cfa v0.0.0\n\nrequire (\n\tcfa/blob v0.0.0 // indirect\n\tgithub.com/Dreamacro/clash v1.7.1 // indirect\n\tgithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34 // indirect\n\tgithub.com/avast/apkparser v0.0.0-20210223100516-186f320f9bfc // indirect\n\tgithub.com/avast/apkverifier v0.0.0-20210916093748-2146ff7c4b7f // indirect\n\tgithub.com/cilium/ebpf v0.9.0 // indirect\n\tgithub.com/dlclark/regexp2 v1.4.0 // indirect\n\tgithub.com/florianl/go-tc v0.4.1 // indirect\n\tgithub.com/gofrs/uuid v4.2.0+incompatible // indirect\n\tgithub.com/google/btree v1.0.1 // indirect\n\tgithub.com/google/go-cmp v0.5.7 // indirect\n\tgithub.com/google/nftables v0.0.0-20220611213346-a346d51f53b3 // indirect\n\tgithub.com/gorilla/websocket v1.5.0 // indirect\n\tgithub.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect\n\tgithub.com/josharian/native v1.0.0 // indirect\n\tgithub.com/klauspost/compress v1.11.13 // indirect\n\tgithub.com/mdlayher/netlink v1.6.0 // indirect\n\tgithub.com/mdlayher/socket v0.1.1 // indirect\n\tgithub.com/miekg/dns v1.1.49 // indirect\n\tgithub.com/openacid/low v0.1.21 // indirect\n\tgithub.com/oschwald/geoip2-golang v1.7.0 // indirect\n\tgithub.com/oschwald/maxminddb-golang v1.9.0 // indirect\n\tgithub.com/samber/lo v1.21.0 // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect\n\tgithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect\n\tgo.etcd.io/bbolt v1.3.6 // indirect\n\tgo.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect\n\tgo4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect\n\tgolang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect\n\tgolang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect\n\tgolang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect\n\tgolang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect\n\tgolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect\n\tgolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgolang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect\n\tgolang.org/x/tools v0.1.9 // indirect\n\tgolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tgvisor.dev/gvisor v0.0.0-20220428010907-8082b77961ba // indirect\n\tinet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect\n)\n\nreplace cfa => ../../main/golang\n\nreplace github.com/Dreamacro/clash => ./clash\n\nreplace cfa/blob => ../../../build/intermediates/golang_blob\n"
  },
  {
    "path": "core/src/premium/golang/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34 h1:USCTqih5d1bUXUxWNS9ZD5Tx/lb0jXHEtRIIx/F9dMc=\ngithub.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34/go.mod h1:YR9wK13TgI5ww8iKWm91MHiSoHC7Oz0U4beCCmtXqLw=\ngithub.com/avast/apkparser v0.0.0-20190516101250-3b8c5efcb6a9/go.mod h1:c0733VBXm1we9M1zCtoOspplSwOYebS3hpDkJyMORRU=\ngithub.com/avast/apkparser v0.0.0-20200102113521-69bcdd9c2403/go.mod h1:eZzHNfZWA1eeKPQE3LVmfRw32lhrH351jDCsma9qxOc=\ngithub.com/avast/apkparser v0.0.0-20200402131724-9fd46d5c4749/go.mod h1:CSBdDZNEsGRYPiDt9QcGrIy8iWQ9YzB1rcuxn44+0jc=\ngithub.com/avast/apkparser v0.0.0-20200924103028-30471fa5618f/go.mod h1:SKNzWGFyNJji/Z+iXjPCpmpFPvenFuhLjrSLCwCM/cM=\ngithub.com/avast/apkparser v0.0.0-20210223100516-186f320f9bfc h1:hiohrweJOE4vk79Wm6JWCBBZBnDmpa55DxSIcyxE0Jw=\ngithub.com/avast/apkparser v0.0.0-20210223100516-186f320f9bfc/go.mod h1:98WPhH/r8MbKpffuuDCAGtPyzSI2IVwXBcWAlXhMVC4=\ngithub.com/avast/apkverifier v0.0.0-20190808142831-dbbe53a24744/go.mod h1:mhWRoMg0KhvWt8SX7B2v2E3VfWt5jWfHfD9PtWAN+qM=\ngithub.com/avast/apkverifier v0.0.0-20200217135742-aa28c80b82ae/go.mod h1:SV58cyAAN+SzX8GIBhizatMJNGcDyfQUj/xZUlKRW+I=\ngithub.com/avast/apkverifier v0.0.0-20200416105355-97c5338f32f0/go.mod h1:HskRSJJJbP3poUkDRAyRAdDVSsh5J1mz8cRc2/B4kbc=\ngithub.com/avast/apkverifier v0.0.0-20210219091843-33631264c352/go.mod h1:uhY/I/3Vh3V6ZFgLm/EFX/j5//MdoXpvcULTtzRW3YA=\ngithub.com/avast/apkverifier v0.0.0-20210916093748-2146ff7c4b7f h1:FMalgGNj/85WMWgR5TJ8OseRSyy/s5QBrRmxOZ8aNAo=\ngithub.com/avast/apkverifier v0.0.0-20210916093748-2146ff7c4b7f/go.mod h1:APQFx11UQTdbLKlZVJQFddZcJZxoHl6NnJfHN7foLD8=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=\ngithub.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=\ngithub.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=\ngithub.com/cilium/ebpf v0.9.0 h1:ldiV+FscPCQ/p3mNEV4O02EPbUZJFsoEtHvIr9xLTvk=\ngithub.com/cilium/ebpf v0.9.0/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=\ngithub.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=\ngithub.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=\ngithub.com/florianl/go-tc v0.4.1 h1:hVx6soOY/wbznLZ5yCNL5Fzc8+fiDr5dQPSIYDQnLV8=\ngithub.com/florianl/go-tc v0.4.1/go.mod h1:kCCrW0ppJu2XcrUYS2utigmcMtEf9qZpAlIg5zdRqXk=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\ngithub.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=\ngithub.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=\ngithub.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=\ngithub.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/nftables v0.0.0-20220611213346-a346d51f53b3 h1:Fq+jS60rvgwyi9zFyGUXwsdNViYcw1tr3CA8ZoYQVEk=\ngithub.com/google/nftables v0.0.0-20220611213346-a346d51f53b3/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=\ngithub.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=\ngithub.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo=\ngithub.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=\ngithub.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=\ngithub.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4=\ngithub.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=\ngithub.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=\ngithub.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=\ngithub.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=\ngithub.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=\ngithub.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=\ngithub.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=\ngithub.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=\ngithub.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=\ngithub.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=\ngithub.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=\ngithub.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=\ngithub.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=\ngithub.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=\ngithub.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=\ngithub.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=\ngithub.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=\ngithub.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=\ngithub.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI=\ngithub.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=\ngithub.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=\ngithub.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=\ngithub.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=\ngithub.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=\ngithub.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=\ngithub.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=\ngithub.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8=\ngithub.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ=\ngithub.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=\ngithub.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/samber/lo v1.21.0 h1:FSby8pJQtX4KmyddTCCGhc3JvnnIVrDA+NW37rG+7G8=\ngithub.com/samber/lo v1.21.0/go.mod h1:2I7tgIv8Q1SG2xEIkRq0F2i2zgxVpnyPOP0d3Gj2r+A=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=\ngithub.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=\ngithub.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=\ngithub.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=\ngithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=\ngithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=\ngo.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=\ngo.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM=\ngo.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=\ngo4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=\ngo4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=\ngo4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=\ngo4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=\ngolang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=\ngolang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=\ngolang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=\ngolang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=\ngolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngvisor.dev/gvisor v0.0.0-20220428010907-8082b77961ba h1:qJ6jWSTl9q+/y4l8QCNpkNnasX/sHzhVnPRysee8PzY=\ngvisor.dev/gvisor v0.0.0-20220428010907-8082b77961ba/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\ninet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU=\ninet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=\n"
  },
  {
    "path": "core/src/premium/golang/main.go",
    "content": "package main\n\nimport _ \"cfa/native/all\"\n"
  },
  {
    "path": "design/build.gradle.kts",
    "content": "plugins {\n    kotlin(\"android\")\n    kotlin(\"kapt\")\n    id(\"com.android.library\")\n}\n\ndependencies {\n    repositories {\n        mavenLocal()\n        mavenCentral()\n        gradlePluginPortal()\n        google()\n        maven(\"https://jitpack.io\")\n        maven(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n        maven(\"https://maven.kr328.app/releases\")\n    }\n    implementation(project(\":common\"))\n    implementation(project(\":core\"))\n    implementation(project(\":service\"))\n\n    implementation(libs.kotlin.coroutine)\n    implementation(libs.androidx.core)\n    implementation(libs.androidx.appcompat)\n    implementation(libs.androidx.activity)\n    implementation(libs.androidx.coordinator)\n    implementation(libs.androidx.recyclerview)\n    implementation(libs.androidx.fragment)\n    implementation(libs.androidx.viewpager)\n    implementation(libs.google.material)\n    implementation(libs.getactivity.xxpermission)\n    implementation(libs.androidx.splashscreen)\n}\n"
  },
  {
    "path": "design/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "design/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.\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"
  },
  {
    "path": "design/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"yos.clash.material.design\" />\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/AccessControlDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport androidx.core.widget.addTextChangedListener\nimport yos.clash.material.design.adapter.AppAdapter\nimport yos.clash.material.design.component.AccessControlMenu\nimport yos.clash.material.design.databinding.DesignAccessControlBinding\nimport yos.clash.material.design.databinding.DialogSearchBinding\nimport yos.clash.material.design.dialog.FullScreenDialog\nimport yos.clash.material.design.model.AppInfo\nimport yos.clash.material.design.store.UiStore\nimport yos.clash.material.design.util.*\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.channels.Channel\n\nclass AccessControlDesign(\n    context: Context,\n    uiStore: UiStore,\n    private val selected: MutableSet<String>,\n) : Design<AccessControlDesign.Request>(context) {\n    enum class Request {\n        ReloadApps,\n        SelectAll,\n        SelectNone,\n        SelectInvert,\n        Import,\n        Export,\n    }\n\n    private val binding = DesignAccessControlBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    private val adapter = AppAdapter(context, selected)\n\n    private val menu: AccessControlMenu by lazy {\n        AccessControlMenu(context, binding.menuView, uiStore, requests)\n    }\n\n    val apps: List<AppInfo>\n        get() = adapter.apps\n\n    override val root: View\n        get() = binding.root\n\n    suspend fun patchApps(apps: List<AppInfo>) {\n        adapter.swapDataSet(adapter::apps, apps, false)\n    }\n\n    suspend fun rebindAll() {\n        withContext(Dispatchers.Main) {\n            adapter.rebindAll()\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.mainList.recyclerList.also {\n            it.bindAppBarElevation(binding.activityBarLayout)\n            it.applyLinearAdapter(context, adapter)\n        }\n\n        binding.menuView.setOnClickListener {\n            menu.show()\n        }\n\n        binding.searchView.setOnClickListener {\n            launch {\n                try {\n                    requestSearch()\n                } finally {\n                    withContext(NonCancellable) {\n                        rebindAll()\n                    }\n                }\n            }\n        }\n    }\n\n    private suspend fun requestSearch() {\n        coroutineScope {\n            val binding = DialogSearchBinding\n                .inflate(context.layoutInflater, context.root, false)\n            val adapter = AppAdapter(context, selected)\n            val dialog = FullScreenDialog(context)\n            val filter = Channel<Unit>(Channel.CONFLATED)\n\n            dialog.setContentView(binding.root)\n\n            binding.surface = dialog.surface\n            binding.mainList.applyLinearAdapter(context, adapter)\n            binding.keywordView.addTextChangedListener {\n                filter.trySend(Unit)\n            }\n            binding.closeView.setOnClickListener {\n                dialog.dismiss()\n            }\n\n            dialog.setOnDismissListener {\n                cancel()\n            }\n\n            dialog.setOnShowListener {\n                binding.keywordView.requestTextInput()\n            }\n\n            dialog.show()\n\n            while (isActive) {\n                filter.receive()\n\n                val keyword = binding.keywordView.text?.toString() ?: \"\"\n\n                val apps: List<AppInfo> = if (keyword.isEmpty()) {\n                    emptyList()\n                } else {\n                    withContext(Dispatchers.Default) {\n                        apps.filter {\n                            it.label.contains(keyword, ignoreCase = true) ||\n                                    it.packageName.contains(keyword, ignoreCase = true)\n                        }\n                    }\n                }\n\n                adapter.patchDataSet(adapter::apps, apps, false, AppInfo::packageName)\n\n                delay(200)\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ApkBrokenDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport yos.clash.material.design.databinding.DesignSettingsCommonBinding\nimport yos.clash.material.design.preference.category\nimport yos.clash.material.design.preference.clickable\nimport yos.clash.material.design.preference.preferenceScreen\nimport yos.clash.material.design.preference.tips\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.bindAppBarElevation\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\nclass ApkBrokenDesign(context: Context) : Design<ApkBrokenDesign.Request>(context) {\n    data class Request(val url: String)\n\n    private val binding = DesignSettingsCommonBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    init {\n        binding.surface = surface\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n\n        val screen = preferenceScreen(context) {\n            tips(R.string.application_broken_tips)\n\n            category(R.string.reinstall)\n\n            clickable(\n                title = R.string.google_play,\n                summary = R.string.google_play_url\n            ) {\n                clicked {\n                    requests.trySend(Request(context.getString(R.string.google_play_url)))\n                }\n            }\n\n            clickable(\n                title = R.string.github_releases,\n                summary = R.string.github_releases_url\n            ) {\n                clicked {\n                    requests.trySend(Request(context.getString(R.string.github_releases_url)))\n                }\n            }\n        }\n\n        binding.content.addView(screen.root)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/AppCrashedDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport yos.clash.material.design.databinding.DesignAppCrashedBinding\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.bindAppBarElevation\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\nclass AppCrashedDesign(context: Context) : Design<Unit>(context) {\n    private val binding = DesignAppCrashedBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    fun setAppLogs(logs: String) {\n        binding.logsView.text = logs\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/AppSettingsDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport com.hjq.permissions.Permission\nimport com.hjq.permissions.XXPermissions\nimport kotlinx.coroutines.withContext\nimport yos.clash.material.design.databinding.DesignSettingsCommonBinding\nimport yos.clash.material.design.model.Behavior\nimport yos.clash.material.design.model.DarkMode\nimport yos.clash.material.design.preference.*\nimport yos.clash.material.design.store.UiStore\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.bindAppBarElevation\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\nimport yos.clash.material.service.store.ServiceStore\n\nclass AppSettingsDesign(\n    context: Context,\n    uiStore: UiStore,\n    srvStore: ServiceStore,\n    behavior: Behavior,\n    running: Boolean,\n) : Design<AppSettingsDesign.Request>(context) {\n    enum class Request {\n        ReCreateAllActivities\n    }\n\n    private val binding = DesignSettingsCommonBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    init {\n        binding.surface = surface\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n\n        val screen = preferenceScreen(context) {\n            category(R.string.behavior)\n\n            switch(\n                value = behavior::autoRestart,\n                icon = R.drawable.ic_baseline_restore,\n                title = R.string.auto_restart,\n                summary = R.string.allow_clash_auto_restart,\n            )\n\n            category(R.string.interface_)\n\n            selectableList(\n                value = uiStore::darkMode,\n                values = DarkMode.values(),\n                valuesText = arrayOf(\n                    R.string.follow_system_android_10,\n                    R.string.always_light,\n                    R.string.always_dark\n                ),\n                icon = R.drawable.ic_baseline_brightness_4,\n                title = R.string.dark_mode\n            ) {\n                listener = OnChangedListener {\n                    requests.trySend(Request.ReCreateAllActivities)\n                }\n            }\n\n            category(R.string.service)\n\n            val permission = XXPermissions.isGranted(context, Permission.POST_NOTIFICATIONS)\n            switch(\n                value = srvStore::dynamicNotification,\n                icon = R.drawable.ic_baseline_domain,\n                title = R.string.show_traffic,\n                summary = if (permission) R.string.show_traffic_summary else R.string.permission_notification_desc\n            ) {\n                enabled = (!running && permission)\n            }\n        }\n\n        binding.content.addView(screen.root)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/Design.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport androidx.appcompat.app.AppCompatActivity\nimport yos.clash.material.design.ui.Surface\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.design.util.setOnInsertsChangedListener\nimport com.google.android.material.snackbar.Snackbar\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.withContext\n\nabstract class Design<R>(val context: Context) :\n    CoroutineScope by CoroutineScope(Dispatchers.Unconfined) {\n    abstract val root: View\n\n    val surface = Surface()\n    val requests: Channel<R> = Channel(Channel.UNLIMITED)\n\n    suspend fun showToast(\n        resId: Int,\n        duration: ToastDuration,\n        configure: Snackbar.() -> Unit = {}\n    ) {\n        return showToast(context.getString(resId), duration, configure)\n    }\n\n    suspend fun showToast(\n        message: CharSequence,\n        duration: ToastDuration,\n        configure: Snackbar.() -> Unit = {}\n    ) {\n        withContext(Dispatchers.Main) {\n            Snackbar.make(\n                root,\n                message,\n                when (duration) {\n                    ToastDuration.Short -> Snackbar.LENGTH_SHORT\n                    ToastDuration.Long -> Snackbar.LENGTH_LONG\n                    ToastDuration.Indefinite -> Snackbar.LENGTH_INDEFINITE\n                }\n            ).apply(configure).show()\n        }\n    }\n\n    init {\n        when (context) {\n            is AppCompatActivity -> {\n                context.window.decorView.setOnInsertsChangedListener {\n                    if (surface.insets != it) {\n                        surface.insets = it\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/FilesDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.view.View\nimport yos.clash.material.design.adapter.FileAdapter\nimport yos.clash.material.design.databinding.DesignFilesBinding\nimport yos.clash.material.design.databinding.DialogFilesMenuBinding\nimport yos.clash.material.design.dialog.AppBottomSheetDialog\nimport yos.clash.material.design.dialog.requestModelTextInput\nimport yos.clash.material.design.model.File\nimport yos.clash.material.design.util.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nclass FilesDesign(context: Context) : Design<FilesDesign.Request>(context) {\n    sealed class Request {\n        data class OpenFile(val file: File) : Request()\n        data class OpenDirectory(val file: File) : Request()\n        data class RenameFile(val file: File) : Request()\n        data class DeleteFile(val file: File) : Request()\n        data class ImportFile(val file: File?) : Request()\n        data class ExportFile(val file: File) : Request()\n\n        object PopStack : Request()\n    }\n\n    private val binding = DesignFilesBinding\n        .inflate(context.layoutInflater, context.root, false)\n    private val adapter: FileAdapter = FileAdapter(context, this::requestOpen, this::requestMore)\n\n    override val root: View\n        get() = binding.root\n\n    var configurationEditable: Boolean\n        get() = binding.configurationEditable\n        set(value) {\n            binding.configurationEditable = value\n        }\n\n    suspend fun swapFiles(files: List<File>, currentInBaseDir: Boolean) {\n        withContext(Dispatchers.Main) {\n            adapter.swapDataSet(adapter::files, files)\n            binding.currentInBaseDir = currentInBaseDir\n        }\n    }\n\n    fun updateElapsed() {\n        adapter.updateElapsed()\n    }\n\n    suspend fun requestFileName(name: String): String {\n        return context.requestModelTextInput(\n            initial = name,\n            title = context.getText(R.string.file_name),\n            hint = context.getText(R.string.file_name),\n            error = context.getText(R.string.invalid_file_name),\n            validator = ValidatorFileName,\n        )\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.mainList.recyclerList.also {\n            it.applyLinearAdapter(context, adapter)\n            it.bindAppBarElevation(binding.activityBarLayout)\n        }\n    }\n\n    private fun requestOpen(file: File) {\n        if (file.isDirectory) {\n            requests.trySend(Request.OpenDirectory(file))\n        } else {\n            requests.trySend(Request.OpenFile(file))\n        }\n    }\n\n    fun requestRename(dialog: Dialog, file: File) {\n        requests.trySend(Request.RenameFile(file))\n\n        dialog.dismiss()\n    }\n\n    fun requestImport(dialog: Dialog, file: File) {\n        requests.trySend(Request.ImportFile(file))\n\n        dialog.dismiss()\n    }\n\n    fun requestExport(dialog: Dialog, file: File) {\n        requests.trySend(Request.ExportFile(file))\n\n        dialog.dismiss()\n    }\n\n    fun requestDelete(dialog: Dialog, file: File) {\n        requests.trySend(Request.DeleteFile(file))\n\n        dialog.dismiss()\n    }\n\n    fun requestNew() {\n        requests.trySend(Request.ImportFile(null))\n    }\n\n    private fun requestMore(file: File) {\n        val dialog = AppBottomSheetDialog(context)\n\n        val binding = DialogFilesMenuBinding.inflate(context.layoutInflater)\n\n        binding.master = this\n        binding.self = dialog\n        binding.file = file\n        binding.currentInBase = this.binding.currentInBaseDir\n        binding.configurationEditable = this.binding.configurationEditable\n\n        dialog.setContentView(binding.root)\n        dialog.show()\n    }\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/HelpDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.net.Uri\nimport android.view.View\nimport yos.clash.material.common.compat.preferredLocale\nimport yos.clash.material.design.databinding.DesignSettingsCommonBinding\nimport yos.clash.material.design.preference.category\nimport yos.clash.material.design.preference.clickable\nimport yos.clash.material.design.preference.preferenceScreen\nimport yos.clash.material.design.preference.tips\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.bindAppBarElevation\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\nclass HelpDesign(\n    context: Context,\n    openLink: (Uri) -> Unit,\n) : Design<Unit>(context) {\n    private val binding = DesignSettingsCommonBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    init {\n        binding.surface = surface\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n\n        val screen = preferenceScreen(context) {\n            tips(R.string.tips_help)\n\n            clickable(\n                title = R.string.application_name,\n                summary = R.string.github_url_now\n            ) {\n                clicked {\n                    openLink(Uri.parse(context.getString(R.string.github_url_now)))\n                }\n            }\n            category(R.string.document)\n\n            clickable(\n                title = R.string.clash_wiki,\n                summary = R.string.clash_wiki_url\n            ) {\n                clicked {\n                    openLink(Uri.parse(context.getString(R.string.clash_wiki_url)))\n                }\n            }\n\n            category(R.string.feedback)\n\n            /*if (YosConfigAchieve.getIfPremium()) {\n                clickable(\n                    title = R.string.google_play,\n                    summary = R.string.google_play_url\n                ) {\n                    clicked {\n                        openLink(Uri.parse(context.getString(R.string.google_play_url)))\n                    }\n                }\n            }*/\n\n            clickable(\n                title = R.string.github_issues,\n                summary = R.string.github_issues_url\n            ) {\n                clicked {\n                    openLink(Uri.parse(context.getString(R.string.github_issues_url)))\n                }\n            }\n\n                category(R.string.sources)\n\n                clickable(\n                    title = R.string.clash_for_android_show,\n                    summary = R.string.github_url\n                ) {\n                    clicked {\n                        openLink(Uri.parse(context.getString(R.string.github_url)))\n                    }\n                }\n\n            clickable(\n                title = R.string.clash_core,\n                summary = R.string.clash_core_url\n            ) {\n                clicked {\n                    openLink(Uri.parse(context.getString(R.string.clash_core_url)))\n                }\n            }\n            category(R.string.donate)\n            clickable(\n                title = R.string.developer_now,\n                summary = R.string.donate_url_now\n            ) {\n                clicked {\n                    openLink(Uri.parse(context.getString(R.string.donate_url_now)))\n                }\n            }\n            if (context.resources.configuration.preferredLocale.language == \"zh\") {\n                clickable(\n                    title = R.string.developer_before,\n                    summary = R.string.donate_url\n                ) {\n                    clicked {\n                        openLink(Uri.parse(context.getString(R.string.donate_url)))\n                    }\n                }\n            }\n        }\n\n        binding.content.addView(screen.root)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/LogcatDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport android.content.Context\nimport android.view.View\nimport androidx.core.content.getSystemService\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.github.kr328.clash.core.model.LogMessage\nimport yos.clash.material.design.adapter.LogMessageAdapter\nimport yos.clash.material.design.databinding.DesignLogcatBinding\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.design.util.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\nclass LogcatDesign(\n    context: Context,\n    private val streaming: Boolean,\n) : Design<LogcatDesign.Request>(context) {\n    enum class Request {\n        Close, Delete, Export\n    }\n\n    private val binding = DesignLogcatBinding\n        .inflate(context.layoutInflater, context.root, false)\n    private val adapter = LogMessageAdapter(context) {\n        launch {\n            val data = ClipData.newPlainText(\"log_message\", it.message)\n\n            context.getSystemService<ClipboardManager>()?.setPrimaryClip(data)\n\n            showToast(R.string.copied, ToastDuration.Short)\n        }\n    }\n\n    suspend fun patchMessages(messages: List<LogMessage>, removed: Int, appended: Int) {\n        withContext(Dispatchers.Main) {\n            adapter.messages = messages\n\n            adapter.notifyItemRangeInserted(adapter.messages.size, appended)\n            adapter.notifyItemRangeRemoved(0, removed)\n\n            if (streaming && binding.recyclerList.isTop) {\n                binding.recyclerList.scrollToPosition(messages.size - 1)\n            }\n        }\n    }\n\n    override val root: View\n        get() = binding.root\n\n    init {\n        binding.self = this\n        binding.streaming = streaming\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.recyclerList.bindAppBarElevation(binding.activityBarLayout)\n\n        binding.recyclerList.layoutManager = LinearLayoutManager(context).apply {\n            if (streaming) {\n                reverseLayout = true\n                stackFromEnd = true\n            }\n        }\n        binding.recyclerList.adapter = adapter\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/LogsDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport yos.clash.material.design.adapter.LogFileAdapter\nimport yos.clash.material.design.databinding.DesignLogsBinding\nimport yos.clash.material.design.model.LogFile\nimport yos.clash.material.design.util.*\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlinx.coroutines.withContext\nimport kotlin.coroutines.resume\n\nclass LogsDesign(context: Context) : Design<LogsDesign.Request>(context) {\n    sealed class Request {\n        object StartLogcat : Request()\n        object DeleteAll : Request()\n\n        data class OpenFile(val file: LogFile) : Request()\n    }\n\n    private val binding = DesignLogsBinding\n        .inflate(context.layoutInflater, context.root, false)\n    private val adapter = LogFileAdapter(context) {\n        requests.trySend(Request.OpenFile(it))\n    }\n\n    override val root: View\n        get() = binding.root\n\n    suspend fun patchLogs(logs: List<LogFile>) {\n        adapter.patchDataSet(adapter::logs, logs, false, LogFile::fileName)\n    }\n\n    suspend fun requestDeleteAll(): Boolean {\n        return withContext(Dispatchers.Main) {\n            suspendCancellableCoroutine { ctx ->\n                MaterialAlertDialogBuilder(context)\n                    .setTitle(R.string.delete_all_logs)\n                    .setMessage(R.string.delete_all_logs_warn)\n                    .setPositiveButton(R.string.ok) { _, _ -> ctx.resume(true) }\n                    .setNegativeButton(R.string.cancel) { _, _ -> }\n                    .show()\n                    .setOnDismissListener { if (!ctx.isCompleted) ctx.resume(false) }\n            }\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.recyclerList.applyLinearAdapter(context, adapter)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/MainDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.app.ProgressDialog.show\nimport android.content.Context\nimport android.graphics.drawable.BitmapDrawable\nimport android.view.View\nimport android.view.WindowManager\nimport androidx.appcompat.app.AlertDialog\nimport com.github.kr328.clash.core.model.TunnelState\nimport com.github.kr328.clash.core.util.trafficTotal\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport com.hjq.permissions.XXPermissions\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport yos.clash.material.design.databinding.DesignAboutBinding\nimport yos.clash.material.design.databinding.DesignMainBinding\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.resolveThemedColor\nimport yos.clash.material.design.util.root\nimport java.security.Permission\n\n\nclass MainDesign(context: Context) : Design<MainDesign.Request>(context) {\n    enum class Request {\n        ToggleStatus,\n        OpenProxy,\n        OpenProfiles,\n        OpenProviders,\n        OpenLogs,\n        OpenSettings,\n        OpenHelp,\n        OpenAbout,\n    }\n\n    private val binding = DesignMainBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    suspend fun setProfileName(name: String?) {\n        withContext(Dispatchers.Main) {\n            binding.profileName = name\n        }\n    }\n\n    suspend fun setClashRunning(running: Boolean) {\n        withContext(Dispatchers.Main) {\n            binding.clashRunning = running\n        }\n    }\n\n    suspend fun setForwarded(value: Long) {\n        withContext(Dispatchers.Main) {\n            binding.forwarded = value.trafficTotal()\n        }\n    }\n\n    suspend fun setMode(mode: TunnelState.Mode) {\n        withContext(Dispatchers.Main) {\n            binding.mode = when (mode) {\n                TunnelState.Mode.Direct -> context.getString(R.string.direct_mode)\n                TunnelState.Mode.Global -> context.getString(R.string.global_mode)\n                TunnelState.Mode.Rule -> context.getString(R.string.rule_mode)\n                TunnelState.Mode.Script -> context.getString(R.string.script_mode)\n            }\n        }\n    }\n\n    suspend fun setHasProviders(has: Boolean) {\n        withContext(Dispatchers.Main) {\n            binding.hasProviders = has\n        }\n    }\n\n    suspend fun showAbout(versionName: String) {\n        withContext(Dispatchers.Main) {\n            val binding = DesignAboutBinding.inflate(context.layoutInflater).apply {\n                this.versionName = versionName\n            }\n\n            val alertDialog = AlertDialog.Builder(context).create()\n            val window = alertDialog.window\n            window!!.setBackgroundDrawable(BitmapDrawable())\n            window.decorView.setPadding(25, 0, 25, 0);\n            alertDialog.setView(binding.root)\n            alertDialog.show()\n        }\n    }\n\n    suspend fun showUpdatedTips() {\n        withContext(Dispatchers.Main) {\n            MaterialAlertDialogBuilder(context)\n                .setTitle(R.string.version_updated)\n                .setMessage(R.string.version_updated_tips)\n                .setPositiveButton(R.string.ok) { _, _ -> }\n                .show()\n        }\n    }\n\n    suspend fun showPermissionRequest() {\n        withContext(Dispatchers.Main) {\n            MaterialAlertDialogBuilder(context)\n                .setTitle(R.string.permission_request_title)\n                .setMessage(R.string.permission_notification_desc)\n                .setPositiveButton(R.string.permission_request_positive) { _, _ ->\n                    XXPermissions.startPermissionActivity(\n                        context,\n                        com.hjq.permissions.Permission.POST_NOTIFICATIONS\n                    )\n                }\n                .setNegativeButton(R.string.permission_request_negative) { _, _ -> }\n                .show()\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.colorClashStarted = context.resolveThemedColor(R.attr.colorPrimary)\n        binding.colorClashStopped = context.resolveThemedColor(R.attr.colorClashStopped)\n    }\n\n    fun request(request: Request) {\n        requests.trySend(request)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/NetworkSettingsDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.os.Build\nimport android.view.View\nimport yos.clash.material.design.databinding.DesignSettingsCommonBinding\nimport yos.clash.material.design.preference.*\nimport yos.clash.material.design.store.UiStore\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.bindAppBarElevation\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\nimport yos.clash.material.service.model.AccessControlMode\nimport yos.clash.material.service.store.ServiceStore\nimport kotlinx.coroutines.launch\n\nclass NetworkSettingsDesign(\n    context: Context,\n    uiStore: UiStore,\n    srvStore: ServiceStore,\n    running: Boolean,\n) : Design<NetworkSettingsDesign.Request>(context) {\n    enum class Request {\n        StartAccessControlList\n    }\n\n    private val binding = DesignSettingsCommonBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    init {\n        binding.surface = surface\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n\n        val screen = preferenceScreen(context) {\n            val vpnDependencies: MutableList<Preference> = mutableListOf()\n\n            val vpn = switch(\n                value = uiStore::enableVpn,\n                icon = R.drawable.ic_baseline_vpn_lock,\n                title = R.string.route_system_traffic,\n                summary = R.string.routing_via_vpn_service\n            ) {\n                listener = OnChangedListener {\n                    vpnDependencies.forEach {\n                        it.enabled = uiStore.enableVpn\n                    }\n                }\n            }\n\n            category(R.string.vpn_service_options)\n\n            switch(\n                value = srvStore::bypassPrivateNetwork,\n                title = R.string.bypass_private_network,\n                summary = R.string.bypass_private_network_summary,\n                configure = vpnDependencies::add,\n            )\n\n            switch(\n                value = srvStore::dnsHijacking,\n                title = R.string.dns_hijacking,\n                summary = R.string.dns_hijacking_summary,\n                configure = vpnDependencies::add,\n            )\n\n            switch(\n                value = srvStore::allowBypass,\n                title = R.string.allow_bypass,\n                summary = R.string.allow_bypass_summary,\n                configure = vpnDependencies::add,\n            )\n\n            if (Build.VERSION.SDK_INT >= 29) {\n                switch(\n                    value = srvStore::systemProxy,\n                    title = R.string.system_proxy,\n                    summary = R.string.system_proxy_summary,\n                    configure = vpnDependencies::add,\n                )\n            }\n\n            selectableList(\n                value = srvStore::accessControlMode,\n                values = AccessControlMode.values(),\n                valuesText = arrayOf(\n                    R.string.allow_all_apps,\n                    R.string.allow_selected_apps,\n                    R.string.deny_selected_apps\n                ),\n                title = R.string.access_control_mode,\n                configure = vpnDependencies::add,\n            )\n\n            clickable(\n                title = R.string.access_control_packages,\n                summary = R.string.access_control_packages_summary,\n            ) {\n                clicked {\n                    requests.trySend(Request.StartAccessControlList)\n                }\n\n                vpnDependencies.add(this)\n            }\n\n            if (running) {\n                vpn.enabled = false\n\n                vpnDependencies.forEach {\n                    it.enabled = false\n                }\n            } else {\n                vpn.listener?.onChanged()\n            }\n        }\n\n        binding.content.addView(screen.root)\n\n        if (running) {\n            launch {\n                showToast(R.string.options_unavailable, ToastDuration.Indefinite)\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/NewProfileDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport yos.clash.material.design.adapter.ProfileProviderAdapter\nimport yos.clash.material.design.databinding.DesignNewProfileBinding\nimport yos.clash.material.design.model.ProfileProvider\nimport yos.clash.material.design.util.*\n\nclass NewProfileDesign(context: Context) : Design<NewProfileDesign.Request>(context) {\n    sealed class Request {\n        data class Create(val provider: ProfileProvider) : Request()\n        data class OpenDetail(val provider: ProfileProvider.External) : Request()\n    }\n\n    private val binding = DesignNewProfileBinding\n        .inflate(context.layoutInflater, context.root, false)\n    private val adapter = ProfileProviderAdapter(context, this::requestCreate, this::requestDetail)\n\n    override val root: View\n        get() = binding.root\n\n    suspend fun patchProviders(providers: List<ProfileProvider>) {\n        adapter.apply {\n            patchDataSet(this::providers, providers)\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.mainList.recyclerList.also {\n            it.bindAppBarElevation(binding.activityBarLayout)\n            it.applyLinearAdapter(context, adapter)\n        }\n    }\n\n    private fun requestCreate(provider: ProfileProvider) {\n        requests.trySend(Request.Create(provider))\n    }\n\n    private fun requestDetail(provider: ProfileProvider): Boolean {\n        if (provider !is ProfileProvider.External) return false\n\n        requests.trySend(Request.OpenDetail(provider))\n\n        return true\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/OverrideSettingsDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport com.github.kr328.clash.core.model.ConfigurationOverride\nimport com.github.kr328.clash.core.model.LogMessage\nimport com.github.kr328.clash.core.model.TunnelState\nimport yos.clash.material.design.adapter.SideloadProviderAdapter\nimport yos.clash.material.design.databinding.DesignSettingsOverideBinding\nimport yos.clash.material.design.databinding.DialogPreferenceListBinding\nimport yos.clash.material.design.dialog.FullScreenDialog\nimport yos.clash.material.design.model.AppInfo\nimport yos.clash.material.design.preference.*\nimport yos.clash.material.design.util.*\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlinx.coroutines.withContext\nimport kotlin.coroutines.resume\n\nclass OverrideSettingsDesign(\n    context: Context,\n    configuration: ConfigurationOverride\n) : Design<OverrideSettingsDesign.Request>(context) {\n    enum class Request {\n        ResetOverride, EditSideloadGeoip\n    }\n\n    private val binding = DesignSettingsOverideBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    suspend fun requestResetConfirm(): Boolean {\n        return suspendCancellableCoroutine { ctx ->\n            val dialog = MaterialAlertDialogBuilder(context)\n                .setTitle(R.string.reset_override_settings)\n                .setMessage(R.string.reset_override_settings_message)\n                .setPositiveButton(R.string.ok) { _, _ -> ctx.resume(true) }\n                .setNegativeButton(R.string.cancel) { _, _ -> }\n                .show()\n\n            dialog.setOnDismissListener {\n                if (!ctx.isCompleted)\n                    ctx.resume(false)\n            }\n\n            ctx.invokeOnCancellation {\n                dialog.dismiss()\n            }\n        }\n    }\n\n    suspend fun requestSelectSideload(initial: String, apps: List<AppInfo>): String =\n        withContext(Dispatchers.Main) {\n            suspendCancellableCoroutine { ctx ->\n                val binding = DialogPreferenceListBinding\n                    .inflate(context.layoutInflater, context.root, false)\n                val adapter = SideloadProviderAdapter(context, apps, initial)\n                val dialog = FullScreenDialog(context)\n\n                dialog.setContentView(binding.root)\n\n                binding.surface = dialog.surface\n\n                binding.titleView.text = context.getString(R.string.sideload_geoip)\n\n                binding.newView.visibility = View.INVISIBLE\n\n                binding.mainList.applyLinearAdapter(context, adapter)\n\n                binding.resetView.setOnClickListener {\n                    ctx.resume(\"\")\n\n                    dialog.dismiss()\n                }\n\n                binding.cancelView.setOnClickListener {\n                    dialog.dismiss()\n                }\n\n                binding.okView.setOnClickListener {\n                    ctx.resume(adapter.selectedPackageName)\n\n                    dialog.dismiss()\n                }\n\n                dialog.setOnDismissListener {\n                    if (!ctx.isCompleted)\n                        ctx.resume(initial)\n                }\n\n                dialog.show()\n            }\n        }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n\n        val booleanValues: Array<Boolean?> = arrayOf(\n            null,\n            true,\n            false\n        )\n        val booleanValuesText: Array<Int> = arrayOf(\n            R.string.dont_modify,\n            R.string.enabled,\n            R.string.disabled\n        )\n\n        val screen = preferenceScreen(context) {\n            category(R.string.general)\n\n            editableText(\n                value = configuration::httpPort,\n                adapter = NullableTextAdapter.Port,\n                title = R.string.http_port,\n                placeholder = R.string.dont_modify,\n                empty = R.string.disabled,\n            )\n\n            editableText(\n                value = configuration::socksPort,\n                adapter = NullableTextAdapter.Port,\n                title = R.string.socks_port,\n                placeholder = R.string.dont_modify,\n                empty = R.string.disabled,\n            )\n\n            editableText(\n                value = configuration::redirectPort,\n                adapter = NullableTextAdapter.Port,\n                title = R.string.redirect_port,\n                placeholder = R.string.dont_modify,\n                empty = R.string.disabled,\n            )\n\n            editableText(\n                value = configuration::tproxyPort,\n                adapter = NullableTextAdapter.Port,\n                title = R.string.tproxy_port,\n                placeholder = R.string.dont_modify,\n                empty = R.string.disabled,\n            )\n\n            editableText(\n                value = configuration::mixedPort,\n                adapter = NullableTextAdapter.Port,\n                title = R.string.mixed_port,\n                placeholder = R.string.dont_modify,\n                empty = R.string.disabled,\n            )\n\n            editableTextList(\n                value = configuration::authentication,\n                adapter = TextAdapter.String,\n                title = R.string.authentication,\n                placeholder = R.string.dont_modify,\n            )\n\n            selectableList(\n                value = configuration::allowLan,\n                values = booleanValues,\n                valuesText = booleanValuesText,\n                title = R.string.allow_lan,\n            )\n\n            selectableList(\n                value = configuration::ipv6,\n                values = booleanValues,\n                valuesText = booleanValuesText,\n                title = R.string.ipv6,\n            )\n\n            editableText(\n                value = configuration::bindAddress,\n                adapter = NullableTextAdapter.String,\n                title = R.string.bind_address,\n                placeholder = R.string.dont_modify,\n                empty = R.string.default_\n            )\n\n            if (YosConfigAchieve.getIfPremium()) {\n                selectableList(\n                    value = configuration::mode,\n                    values = arrayOf(\n                        null,\n                        TunnelState.Mode.Direct,\n                        TunnelState.Mode.Global,\n                        TunnelState.Mode.Rule,\n                        TunnelState.Mode.Script\n                    ),\n                    valuesText = arrayOf(\n                        R.string.dont_modify,\n                        R.string.direct_mode,\n                        R.string.global_mode,\n                        R.string.rule_mode,\n                        R.string.script_mode\n                    ),\n                    title = R.string.mode\n                )\n            } else {\n                selectableList(\n                    value = configuration::mode,\n                    values = arrayOf(\n                        null,\n                        TunnelState.Mode.Direct,\n                        TunnelState.Mode.Global,\n                        TunnelState.Mode.Rule\n                    ),\n                    valuesText = arrayOf(\n                        R.string.dont_modify,\n                        R.string.direct_mode,\n                        R.string.global_mode,\n                        R.string.rule_mode\n                    ),\n                    title = R.string.mode\n                )\n            }\n\n            if (YosConfigAchieve.getIfPremium()) {\n                selectableList(\n                    value = configuration.experimental::sniffTLSSNI,\n                    values = booleanValues,\n                    valuesText = booleanValuesText,\n                    title = R.string.sniff_tls_sni,\n                )\n            }\n\n            selectableList(\n                value = configuration::logLevel,\n                values = arrayOf(\n                    null,\n                    LogMessage.Level.Info,\n                    LogMessage.Level.Warning,\n                    LogMessage.Level.Error,\n                    LogMessage.Level.Debug,\n                    LogMessage.Level.Silent,\n                ),\n                valuesText = arrayOf(\n                    R.string.dont_modify,\n                    R.string.info,\n                    R.string.warning,\n                    R.string.error,\n                    R.string.debug,\n                    R.string.silent,\n                ),\n                title = R.string.log_level,\n            )\n\n            editableTextMap(\n                value = configuration::hosts,\n                keyAdapter = TextAdapter.String,\n                valueAdapter = TextAdapter.String,\n                title = R.string.hosts,\n                placeholder = R.string.dont_modify,\n            )\n\n            clickable(\n                title = R.string.sideload_geoip,\n                summary = R.string.sideload_geoip_summary\n            ) {\n                clicked {\n                    requests.trySend(Request.EditSideloadGeoip)\n                }\n            }\n\n            category(R.string.dns)\n\n            val dnsDependencies: MutableList<Preference> = mutableListOf()\n\n            val dns = selectableList(\n                value = configuration.dns::enable,\n                values = arrayOf(\n                    null,\n                    true,\n                    false\n                ),\n                valuesText = arrayOf(\n                    R.string.dont_modify,\n                    R.string.force_enable,\n                    R.string.use_built_in,\n                ),\n                title = R.string.strategy\n            ) {\n                listener = OnChangedListener {\n                    if (configuration.dns.enable == false) {\n                        dnsDependencies.forEach {\n                            it.enabled = false\n                        }\n                    } else {\n                        dnsDependencies.forEach {\n                            it.enabled = true\n                        }\n                    }\n                }\n            }\n\n            editableText(\n                value = configuration.dns::listen,\n                adapter = NullableTextAdapter.String,\n                title = R.string.listen,\n                placeholder = R.string.dont_modify,\n                empty = R.string.disabled,\n                configure = dnsDependencies::add,\n            )\n\n            selectableList(\n                value = configuration.app::appendSystemDns,\n                values = booleanValues,\n                valuesText = booleanValuesText,\n                title = R.string.append_system_dns,\n                configure = dnsDependencies::add,\n            )\n\n            selectableList(\n                value = configuration.dns::ipv6,\n                values = booleanValues,\n                valuesText = booleanValuesText,\n                title = R.string.ipv6,\n                configure = dnsDependencies::add,\n            )\n\n            selectableList(\n                value = configuration.dns::useHosts,\n                values = booleanValues,\n                valuesText = booleanValuesText,\n                title = R.string.use_hosts,\n                configure = dnsDependencies::add,\n            )\n\n            selectableList(\n                value = configuration.dns::enhancedMode,\n                values = arrayOf(\n                    null,\n                    ConfigurationOverride.DnsEnhancedMode.None,\n                    ConfigurationOverride.DnsEnhancedMode.FakeIp,\n                    ConfigurationOverride.DnsEnhancedMode.Mapping\n                ),\n                valuesText = arrayOf(\n                    R.string.dont_modify,\n                    R.string.disabled,\n                    R.string.fakeip,\n                    R.string.mapping\n                ),\n                title = R.string.enhanced_mode,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextList(\n                value = configuration.dns::nameServer,\n                adapter = TextAdapter.String,\n                title = R.string.name_server,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextList(\n                value = configuration.dns::fallback,\n                adapter = TextAdapter.String,\n                title = R.string.fallback,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextList(\n                value = configuration.dns::defaultServer,\n                adapter = TextAdapter.String,\n                title = R.string.default_name_server,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextList(\n                value = configuration.dns::fakeIpFilter,\n                adapter = TextAdapter.String,\n                title = R.string.fakeip_filter,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            selectableList(\n                value = configuration.dns.fallbackFilter::geoIp,\n                values = booleanValues,\n                valuesText = booleanValuesText,\n                title = R.string.geoip_fallback,\n                configure = dnsDependencies::add,\n            )\n\n            editableText(\n                value = configuration.dns.fallbackFilter::geoIpCode,\n                adapter = NullableTextAdapter.String,\n                title = R.string.geoip_fallback_code,\n                placeholder = R.string.dont_modify,\n                empty = R.string.raw_cn,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextList(\n                value = configuration.dns.fallbackFilter::domain,\n                adapter = TextAdapter.String,\n                title = R.string.domain_fallback,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextList(\n                value = configuration.dns.fallbackFilter::ipcidr,\n                adapter = TextAdapter.String,\n                title = R.string.ipcidr_fallback,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            editableTextMap(\n                value = configuration.dns::nameserverPolicy,\n                keyAdapter = TextAdapter.String,\n                valueAdapter = TextAdapter.String,\n                title = R.string.name_server_policy,\n                placeholder = R.string.dont_modify,\n                configure = dnsDependencies::add,\n            )\n\n            dns.listener?.onChanged()\n        }\n\n        binding.content.addView(screen.root)\n    }\n\n    fun requestClear() {\n        requests.trySend(Request.ResetOverride)\n    }\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ProfilesDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.view.View\nimport android.view.ViewGroup\nimport yos.clash.material.design.adapter.ProfileAdapter\nimport yos.clash.material.design.databinding.DesignProfilesBinding\nimport yos.clash.material.design.databinding.DialogProfilesMenuBinding\nimport yos.clash.material.design.dialog.AppBottomSheetDialog\nimport yos.clash.material.design.ui.ToastDuration\nimport yos.clash.material.design.util.*\nimport yos.clash.material.service.model.Profile\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nclass ProfilesDesign(context: Context) : Design<ProfilesDesign.Request>(context) {\n    sealed class Request {\n        object UpdateAll : Request()\n        object Create : Request()\n        data class Active(val profile: Profile) : Request()\n        data class Update(val profile: Profile) : Request()\n        data class Edit(val profile: Profile) : Request()\n        data class Duplicate(val profile: Profile) : Request()\n        data class Delete(val profile: Profile) : Request()\n    }\n\n    private val binding = DesignProfilesBinding\n        .inflate(context.layoutInflater, context.root, false)\n    private val adapter = ProfileAdapter(context, this::requestActive, this::showMenu)\n\n    override val root: View\n        get() = binding.root\n\n    suspend fun patchProfiles(profiles: List<Profile>) {\n        adapter.apply {\n            patchDataSet(this::profiles, profiles, id = { it.uuid })\n        }\n\n        val updatable = withContext(Dispatchers.Default) {\n            profiles.any { it.imported && it.type != Profile.Type.File }\n        }\n\n        withContext(Dispatchers.Main) {\n            binding.updateView.visibility = if (updatable) View.VISIBLE else View.GONE\n        }\n    }\n\n    suspend fun requestSave(profile: Profile) {\n        showToast(R.string.active_unsaved_tips, ToastDuration.Long) {\n            setAction(R.string.edit) {\n                requests.trySend(Request.Edit(profile))\n            }\n        }\n    }\n\n    fun updateElapsed() {\n        adapter.updateElapsed()\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.mainList.recyclerList.also {\n            it.bindAppBarElevation(binding.activityBarLayout)\n            it.applyLinearAdapter(context, adapter)\n        }\n    }\n\n    private fun showMenu(profile: Profile) {\n        val dialog = AppBottomSheetDialog(context)\n\n        val binding = DialogProfilesMenuBinding\n            .inflate(context.layoutInflater, dialog.window?.decorView as ViewGroup?, false)\n\n        binding.master = this\n        binding.self = dialog\n        binding.profile = profile\n\n        dialog.setContentView(binding.root)\n        dialog.show()\n    }\n\n    fun requestUpdateAll() {\n        requests.trySend(Request.UpdateAll)\n    }\n\n    fun requestCreate() {\n        requests.trySend(Request.Create)\n    }\n\n    private fun requestActive(profile: Profile) {\n        requests.trySend(Request.Active(profile))\n    }\n\n    fun requestUpdate(dialog: Dialog, profile: Profile) {\n        requests.trySend(Request.Update(profile))\n\n        dialog.dismiss()\n    }\n\n    fun requestEdit(dialog: Dialog, profile: Profile) {\n        requests.trySend(Request.Edit(profile))\n\n        dialog.dismiss()\n    }\n\n    fun requestDuplicate(dialog: Dialog, profile: Profile) {\n        requests.trySend(Request.Duplicate(profile))\n\n        dialog.dismiss()\n    }\n\n    fun requestDelete(dialog: Dialog, profile: Profile) {\n        requests.trySend(Request.Delete(profile))\n\n        dialog.dismiss()\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/PropertiesDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport com.github.kr328.clash.core.model.FetchStatus\nimport yos.clash.material.design.databinding.DesignPropertiesBinding\nimport yos.clash.material.design.dialog.ModelProgressBarConfigure\nimport yos.clash.material.design.dialog.requestModelTextInput\nimport yos.clash.material.design.dialog.withModelProgressBar\nimport yos.clash.material.design.util.*\nimport yos.clash.material.service.model.Profile\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlinx.coroutines.withContext\nimport java.util.concurrent.TimeUnit\nimport kotlin.coroutines.resume\n\nclass PropertiesDesign(context: Context) : Design<PropertiesDesign.Request>(context) {\n    sealed class Request {\n        object Commit : Request()\n        object BrowseFiles : Request()\n    }\n\n    private val binding = DesignPropertiesBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    var profile: Profile\n        get() = binding.profile!!\n        set(value) {\n            binding.profile = value\n        }\n\n    val progressing: Boolean\n        get() = binding.processing\n\n    suspend fun withProcessing(executeTask: suspend (suspend (FetchStatus) -> Unit) -> Unit) {\n        try {\n            binding.processing = true\n\n            context.withModelProgressBar {\n                configure {\n                    isIndeterminate = true\n                    text = context.getString(R.string.initializing)\n                }\n\n                executeTask {\n                    configure {\n                        applyFrom(it)\n                    }\n                }\n            }\n        } finally {\n            binding.processing = false\n        }\n    }\n\n    suspend fun requestExitWithoutSaving(): Boolean {\n        return withContext(Dispatchers.Main) {\n            suspendCancellableCoroutine { ctx ->\n                val dialog = MaterialAlertDialogBuilder(context)\n                    .setTitle(R.string.exit_without_save)\n                    .setMessage(R.string.exit_without_save_warning)\n                    .setCancelable(true)\n                    .setPositiveButton(R.string.ok) { _, _ -> ctx.resume(true) }\n                    .setNegativeButton(R.string.cancel) { _, _ -> }\n                    .setOnDismissListener { if (!ctx.isCompleted) ctx.resume(false) }\n                    .show()\n\n                ctx.invokeOnCancellation { dialog.dismiss() }\n            }\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.tips.text = context.getHtml(R.string.tips_properties)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n    }\n\n    fun inputName() {\n        launch {\n            val name = context.requestModelTextInput(\n                initial = profile.name,\n                title = context.getText(R.string.name),\n                hint = context.getText(R.string.properties),\n                error = context.getText(R.string.should_not_be_blank),\n                validator = ValidatorNotBlank\n            )\n\n            if (name != profile.name) {\n                profile = profile.copy(name = name)\n            }\n        }\n    }\n\n    fun inputUrl() {\n        if (profile.type == Profile.Type.External)\n            return\n\n        launch {\n            val url = context.requestModelTextInput(\n                initial = profile.source,\n                title = context.getText(R.string.url),\n                hint = context.getText(R.string.profile_url),\n                error = context.getText(R.string.accept_http_content),\n                validator = ValidatorHttpUrl\n            )\n\n            if (url != profile.source) {\n                profile = profile.copy(source = url)\n            }\n        }\n    }\n\n    fun inputInterval() {\n        launch {\n            var minutes = TimeUnit.MILLISECONDS.toMinutes(profile.interval)\n\n            minutes = context.requestModelTextInput(\n                initial = if (minutes == 0L) \"\" else minutes.toString(),\n                title = context.getText(R.string.auto_update),\n                hint = context.getText(R.string.auto_update_minutes),\n                error = context.getText(R.string.at_least_15_minutes),\n                validator = ValidatorAutoUpdateInterval\n            ).toLongOrNull() ?: 0\n\n            val interval = TimeUnit.MINUTES.toMillis(minutes)\n\n            if (interval != profile.interval) {\n                profile = profile.copy(interval = interval)\n            }\n        }\n    }\n\n    fun requestCommit() {\n        requests.trySend(Request.Commit)\n    }\n\n    fun requestBrowseFiles() {\n        requests.trySend(Request.BrowseFiles)\n    }\n\n    private fun ModelProgressBarConfigure.applyFrom(status: FetchStatus) {\n        when (status.action) {\n            FetchStatus.Action.FetchConfiguration -> {\n                text = context.getString(R.string.format_fetching_configuration, status.args[0])\n                isIndeterminate = true\n            }\n            FetchStatus.Action.FetchProviders -> {\n                text = context.getString(R.string.format_fetching_provider, status.args[0])\n                isIndeterminate = false\n                max = status.max\n                progress = status.progress\n            }\n            FetchStatus.Action.Verifying -> {\n                text = context.getString(R.string.verifying)\n                isIndeterminate = false\n                max = status.max\n                progress = status.progress\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ProvidersDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport com.github.kr328.clash.core.model.Provider\nimport yos.clash.material.design.adapter.ProviderAdapter\nimport yos.clash.material.design.databinding.DesignProvidersBinding\nimport yos.clash.material.design.util.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nclass ProvidersDesign(\n    context: Context,\n    providers: List<Provider>,\n) : Design<ProvidersDesign.Request>(context) {\n    sealed class Request {\n        data class Update(val index: Int, val provider: Provider) : Request()\n    }\n\n    private val binding = DesignProvidersBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    private val adapter = ProviderAdapter(context, providers) { index, provider ->\n        requests.trySend(Request.Update(index, provider))\n    }\n\n    fun updateElapsed() {\n        adapter.updateElapsed()\n    }\n\n    suspend fun notifyUpdated(index: Int) {\n        withContext(Dispatchers.Main) {\n            adapter.notifyUpdated(index)\n        }\n    }\n\n    suspend fun notifyChanged(index: Int) {\n        withContext(Dispatchers.Main) {\n            adapter.notifyChanged(index)\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.mainList.recyclerList.bindAppBarElevation(binding.activityBarLayout)\n        binding.mainList.recyclerList.applyLinearAdapter(context, adapter)\n    }\n\n    fun requestUpdateAll() {\n        adapter.states.filter { !it.updating }.forEachIndexed { index, state ->\n            state.updating = true\n\n            requests.trySend(Request.Update(index, state.provider))\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ProxyDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.view.View\nimport android.widget.Toast\nimport androidx.viewpager2.widget.ViewPager2\nimport com.github.kr328.clash.core.model.Proxy\nimport com.github.kr328.clash.core.model.TunnelState\nimport yos.clash.material.design.adapter.ProxyAdapter\nimport yos.clash.material.design.adapter.ProxyPageAdapter\nimport yos.clash.material.design.component.ProxyMenu\nimport yos.clash.material.design.component.ProxyViewConfig\nimport yos.clash.material.design.databinding.DesignProxyBinding\nimport yos.clash.material.design.model.ProxyState\nimport yos.clash.material.design.store.UiStore\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.resolveThemedColor\nimport yos.clash.material.design.util.root\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport com.google.android.material.tabs.TabLayoutMediator\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nclass ProxyDesign(\n    context: Context,\n    overrideMode: TunnelState.Mode?,\n    groupNames: List<String>,\n    uiStore: UiStore,\n) : Design<ProxyDesign.Request>(context) {\n    sealed class Request {\n        object ReloadAll : Request()\n        object ReLaunch : Request()\n\n        data class PatchMode(val mode: TunnelState.Mode?) : Request()\n        data class Reload(val index: Int) : Request()\n        data class Select(val index: Int, val name: String) : Request()\n        data class UrlTest(val index: Int) : Request()\n    }\n\n    private val binding = DesignProxyBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    private val config = ProxyViewConfig(context, uiStore.proxySingleLine)\n\n    private val menu: ProxyMenu by lazy {\n        ProxyMenu(context, binding.menuView, overrideMode, uiStore, requests) {\n            config.singleLine = uiStore.proxySingleLine\n        }\n    }\n\n    private val adapter: ProxyPageAdapter\n        get() = binding.pagesView.adapter!! as ProxyPageAdapter\n\n    private var horizontalScrolling = false\n    private val verticalBottomScrolled: Boolean\n        get() = adapter.states[binding.pagesView.currentItem].bottom\n    private var urlTesting: Boolean\n        get() = adapter.states[binding.pagesView.currentItem].urlTesting\n        set(value) {\n            adapter.states[binding.pagesView.currentItem].urlTesting = value\n        }\n\n    override val root: View = binding.root\n\n    suspend fun updateGroup(\n        position: Int,\n        proxies: List<Proxy>,\n        selectable: Boolean,\n        parent: ProxyState,\n        links: Map<String, ProxyState>\n    ) {\n        adapter.updateAdapter(position, proxies, selectable, parent, links)\n\n        adapter.states[position].urlTesting = false\n\n        updateUrlTestButtonStatus()\n    }\n\n    suspend fun requestRedrawVisible() {\n        withContext(Dispatchers.Main) {\n            adapter.requestRedrawVisible()\n        }\n    }\n\n    suspend fun requestDonate() {\n        withContext(Dispatchers.Main) {\n            val title = context.getText(R.string.request_donate)\n            val message = context.getText(R.string.request_donate_tips)\n\n            if (title.isNotEmpty() && message.isNotEmpty()) {\n                MaterialAlertDialogBuilder(context)\n                    .setTitle(R.string.request_donate)\n                    .setMessage(R.string.request_donate_tips)\n                    .setPositiveButton(R.string.ok) { _, _ -> }\n                    .setCancelable(true)\n                    .show()\n            }\n        }\n    }\n\n    suspend fun showModeSwitchTips() {\n        withContext(Dispatchers.Main) {\n            Toast.makeText(context, R.string.mode_switch_tips, Toast.LENGTH_LONG).show()\n        }\n    }\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.menuView.setOnClickListener {\n            menu.show()\n        }\n\n        if (groupNames.isEmpty()) {\n            binding.emptyView.visibility = View.VISIBLE\n\n            binding.urlTestView.visibility = View.GONE\n            binding.tabLayoutView.visibility = View.GONE\n            binding.elevationView.visibility = View.GONE\n            binding.pagesView.visibility = View.GONE\n            binding.urlTestFloatView.visibility = View.GONE\n        } else {\n            binding.urlTestFloatView.supportImageTintList = ColorStateList.valueOf(\n                context.resolveThemedColor(R.attr.colorOnPrimary)\n            )\n\n            binding.pagesView.apply {\n                adapter = ProxyPageAdapter(\n                    surface,\n                    config,\n                    List(groupNames.size) { index ->\n                        ProxyAdapter(config) { name ->\n                            requests.trySend(Request.Select(index, name))\n                        }\n                    }\n                ) {\n                    if (it == currentItem)\n                        updateUrlTestButtonStatus()\n                }\n\n                registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {\n                    override fun onPageScrollStateChanged(state: Int) {\n                        horizontalScrolling = state != ViewPager2.SCROLL_STATE_IDLE\n\n                        updateUrlTestButtonStatus()\n                    }\n\n                    override fun onPageSelected(position: Int) {\n                        uiStore.proxyLastGroup = groupNames[position]\n                    }\n                })\n            }\n\n            TabLayoutMediator(binding.tabLayoutView, binding.pagesView) { tab, index ->\n                tab.text = groupNames[index]\n            }.attach()\n\n            val initialPosition = groupNames.indexOf(uiStore.proxyLastGroup)\n\n            binding.pagesView.post {\n                if (initialPosition > 0)\n                    binding.pagesView.setCurrentItem(initialPosition, false)\n            }\n        }\n    }\n\n    fun requestUrlTesting() {\n        urlTesting = true\n\n        requests.trySend(Request.UrlTest(binding.pagesView.currentItem))\n\n        updateUrlTestButtonStatus()\n    }\n\n    private fun updateUrlTestButtonStatus() {\n        if (verticalBottomScrolled || horizontalScrolling || urlTesting) {\n            binding.urlTestFloatView.hide()\n        } else {\n            binding.urlTestFloatView.show()\n        }\n\n        if (urlTesting) {\n            binding.urlTestView.visibility = View.GONE\n            binding.urlTestProgressView.visibility = View.VISIBLE\n        } else {\n            binding.urlTestView.visibility = View.VISIBLE\n            binding.urlTestProgressView.visibility = View.GONE\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/SettingsDesign.kt",
    "content": "package yos.clash.material.design\n\nimport android.content.Context\nimport android.view.View\nimport yos.clash.material.design.databinding.DesignSettingsBinding\nimport yos.clash.material.design.util.applyFrom\nimport yos.clash.material.design.util.bindAppBarElevation\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\nclass SettingsDesign(context: Context) : Design<SettingsDesign.Request>(context) {\n    enum class Request {\n        StartApp, StartNetwork, StartOverride,\n    }\n\n    private val binding = DesignSettingsBinding\n        .inflate(context.layoutInflater, context.root, false)\n\n    override val root: View\n        get() = binding.root\n\n    init {\n        binding.self = this\n\n        binding.activityBarLayout.applyFrom(context)\n\n        binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)\n    }\n\n    fun request(request: Request) {\n        requests.trySend(request)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/YosConfigAchieve.kt",
    "content": "package yos.clash.material.design\n\nopen class YosConfigAchieve {\n    companion object {\n        fun getIfPremium(): Boolean {\n            return true\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/AppAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterAppBinding\nimport yos.clash.material.design.model.AppInfo\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\nclass AppAdapter(\n    private val context: Context,\n    private val selected: MutableSet<String>,\n) : RecyclerView.Adapter<AppAdapter.Holder>() {\n    class Holder(val binding: AdapterAppBinding) : RecyclerView.ViewHolder(binding.root)\n\n    var apps: List<AppInfo> = emptyList()\n\n    fun rebindAll() {\n        notifyItemRangeChanged(0, itemCount)\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterAppBinding\n                .inflate(context.layoutInflater, context.root, false)\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = apps[position]\n\n        holder.binding.app = current\n        holder.binding.selected = current.packageName in selected\n        holder.binding.root.setOnClickListener {\n            if (holder.binding.selected) {\n                selected.remove(current.packageName)\n                holder.binding.selected = false\n            } else {\n                selected.add(current.packageName)\n                holder.binding.selected = true\n            }\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return apps.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/EditableTextListAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterEditableTextListBinding\nimport yos.clash.material.design.preference.TextAdapter\nimport yos.clash.material.design.util.layoutInflater\n\nclass EditableTextListAdapter<T>(\n    private val context: Context,\n    val values: MutableList<T>,\n    private val adapter: TextAdapter<T>,\n) : RecyclerView.Adapter<EditableTextListAdapter.Holder>() {\n    class Holder(val binding: AdapterEditableTextListBinding) :\n        RecyclerView.ViewHolder(binding.root)\n\n    fun addElement(text: String) {\n        val value = adapter.to(text)\n\n        notifyItemInserted(values.size)\n        values.add(value)\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterEditableTextListBinding\n                .inflate(context.layoutInflater, parent, false)\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = values[position]\n\n        holder.binding.textView.text = adapter.from(current)\n        holder.binding.deleteView.setOnClickListener {\n            val index = values.indexOf(current)\n\n            if (index >= 0) {\n                values.removeAt(index)\n                notifyItemRemoved(index)\n            }\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return values.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/EditableTextMapAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterEditableTextMapBinding\nimport yos.clash.material.design.preference.TextAdapter\nimport yos.clash.material.design.util.layoutInflater\n\nclass EditableTextMapAdapter<K, V>(\n    private val context: Context,\n    val values: MutableList<Pair<K, V>>,\n    private val keyAdapter: TextAdapter<K>,\n    private val valueAdapter: TextAdapter<V>,\n) : RecyclerView.Adapter<EditableTextMapAdapter.Holder>() {\n    class Holder(val binding: AdapterEditableTextMapBinding) : RecyclerView.ViewHolder(binding.root)\n\n    fun addElement(key: String, value: String) {\n        val keyValue = keyAdapter.to(key)\n        val valueValue = valueAdapter.to(value)\n\n        notifyItemInserted(values.size)\n        values.add(keyValue to valueValue)\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterEditableTextMapBinding\n                .inflate(context.layoutInflater, parent, false)\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = values[position]\n\n        holder.binding.keyView.text = keyAdapter.from(current.first)\n        holder.binding.valueView.text = valueAdapter.from(current.second)\n        holder.binding.deleteView.setOnClickListener {\n            val index = values.indexOf(current)\n\n            if (index >= 0) {\n                values.removeAt(index)\n                notifyItemRemoved(index)\n            }\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return values.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/FileAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterFileBinding\nimport yos.clash.material.design.model.File\nimport yos.clash.material.design.ui.ObservableCurrentTime\nimport yos.clash.material.design.util.layoutInflater\n\nclass FileAdapter(\n    private val context: Context,\n    private val open: (File) -> Unit,\n    private val more: (File) -> Unit,\n) : RecyclerView.Adapter<FileAdapter.Holder>() {\n    class Holder(val binding: AdapterFileBinding) : RecyclerView.ViewHolder(binding.root)\n\n    private val currentTime = ObservableCurrentTime()\n\n    var files: List<File> = emptyList()\n\n    fun updateElapsed() {\n        currentTime.update()\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterFileBinding\n                .inflate(context.layoutInflater, parent, false)\n                .also { it.currentTime = currentTime }\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = files[position]\n\n        holder.binding.apply {\n            file = current\n\n            setOpen {\n                open(current)\n            }\n\n            setMore {\n                more(current)\n            }\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return files.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/LogFileAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport android.view.ViewGroup.LayoutParams\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.model.LogFile\nimport yos.clash.material.design.util.format\nimport yos.clash.material.design.view.ActionLabel\n\nclass LogFileAdapter(\n    private val context: Context,\n    private val open: (LogFile) -> Unit,\n) : RecyclerView.Adapter<LogFileAdapter.Holder>() {\n    class Holder(val label: ActionLabel) : RecyclerView.ViewHolder(label)\n\n    var logs: List<LogFile> = emptyList()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(ActionLabel(context).apply {\n            layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)\n        })\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = logs[position]\n\n        holder.label.text = current.fileName\n        holder.label.subtext = current.date.format(context)\n        holder.label.setOnClickListener {\n            open(current)\n        }\n        holder.label.icon = null\n    }\n\n    override fun getItemCount(): Int {\n        return logs.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/LogMessageAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport com.github.kr328.clash.core.model.LogMessage\nimport yos.clash.material.design.databinding.AdapterLogMessageBinding\nimport yos.clash.material.design.util.layoutInflater\n\nclass LogMessageAdapter(\n    private val context: Context,\n    private val copy: (LogMessage) -> Unit,\n) :\n    RecyclerView.Adapter<LogMessageAdapter.Holder>() {\n    class Holder(val binding: AdapterLogMessageBinding) : RecyclerView.ViewHolder(binding.root)\n\n    var messages: List<LogMessage> = emptyList()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterLogMessageBinding\n                .inflate(context.layoutInflater, parent, false)\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = messages[position]\n\n        holder.binding.message = current\n        holder.binding.root.setOnLongClickListener {\n            copy(current)\n\n            true\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return messages.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/PopupListAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.BaseAdapter\nimport android.widget.TextView\nimport yos.clash.material.design.R\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.resolveThemedColor\n\nclass PopupListAdapter(\n    private val context: Context,\n    private val texts: List<CharSequence>,\n    private val selected: Int,\n) : BaseAdapter() {\n    private val colorPrimary = context.resolveThemedColor(R.attr.colorPrimary)\n    private val colorOnPrimary = context.resolveThemedColor(R.attr.colorOnPrimary)\n    private val colorControlNormal = context.resolveThemedColor(R.attr.colorControlNormal)\n\n    override fun getCount(): Int {\n        return texts.size\n    }\n\n    override fun getItem(position: Int): Any {\n        return texts[position]\n    }\n\n    override fun getItemId(position: Int): Long {\n        return texts[position].hashCode().toLong()\n    }\n\n    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {\n        val view = convertView ?: context.layoutInflater\n            .inflate(android.R.layout.simple_list_item_1, parent, false)\n\n        val text: TextView = view.findViewById(android.R.id.text1)\n\n        text.text = texts[position]\n\n        if (position == selected) {\n            text.setBackgroundColor(\n                Color.argb(\n                    200,\n                    Color.red(colorPrimary),\n                    Color.green(colorPrimary),\n                    Color.blue(colorPrimary)\n                )\n            )\n            text.setTextColor(colorOnPrimary)\n        } else {\n            text.setBackgroundColor(Color.TRANSPARENT)\n            text.setTextColor(colorControlNormal)\n        }\n\n        return view\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/ProfileAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterProfileBinding\nimport yos.clash.material.design.ui.ObservableCurrentTime\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.service.model.Profile\n\nclass ProfileAdapter(\n    private val context: Context,\n    private val onClicked: (Profile) -> Unit,\n    private val onMenuClicked: (Profile) -> Unit,\n) : RecyclerView.Adapter<ProfileAdapter.Holder>() {\n    class Holder(val binding: AdapterProfileBinding) : RecyclerView.ViewHolder(binding.root)\n\n    private val currentTime = ObservableCurrentTime()\n\n    var profiles: List<Profile> = emptyList()\n\n    fun updateElapsed() {\n        currentTime.update()\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterProfileBinding\n                .inflate(context.layoutInflater, parent, false)\n                .also { it.currentTime = currentTime }\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = profiles[position]\n        val binding = holder.binding\n\n        if (current === binding.profile)\n            return\n\n        binding.profile = current\n        binding.setClicked {\n            onClicked(current)\n        }\n        binding.setMenu {\n            onMenuClicked(current)\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return profiles.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/ProfileProviderAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterProfileProviderBinding\nimport yos.clash.material.design.model.ProfileProvider\nimport yos.clash.material.design.util.layoutInflater\n\nclass ProfileProviderAdapter(\n    private val context: Context,\n    private val select: (ProfileProvider) -> Unit,\n    private val detail: (ProfileProvider) -> Boolean,\n) : RecyclerView.Adapter<ProfileProviderAdapter.Holder>() {\n    class Holder(val binding: AdapterProfileProviderBinding) : RecyclerView.ViewHolder(binding.root)\n\n    var providers: List<ProfileProvider> = emptyList()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterProfileProviderBinding.inflate(\n                context.layoutInflater,\n                parent,\n                false\n            )\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = providers[position]\n        val binding = holder.binding\n\n        binding.provider = current\n\n        binding.root.apply {\n            setOnClickListener {\n                select(current)\n            }\n            setOnLongClickListener {\n                detail(current)\n            }\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return providers.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/ProviderAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport com.github.kr328.clash.core.model.Provider\nimport yos.clash.material.design.databinding.AdapterProviderBinding\nimport yos.clash.material.design.model.ProviderState\nimport yos.clash.material.design.ui.ObservableCurrentTime\nimport yos.clash.material.design.util.layoutInflater\n\nclass ProviderAdapter(\n    private val context: Context,\n    providers: List<Provider>,\n    private val requestUpdate: (Int, Provider) -> Unit,\n) : RecyclerView.Adapter<ProviderAdapter.Holder>() {\n    class Holder(val binding: AdapterProviderBinding) : RecyclerView.ViewHolder(binding.root)\n\n    private val currentTime = ObservableCurrentTime()\n\n    val states = providers.map { ProviderState(it, it.updatedAt, false) }\n\n    fun updateElapsed() {\n        currentTime.update()\n    }\n\n    fun notifyUpdated(index: Int) {\n        states[index].apply {\n            updating = false\n        }\n\n        notifyItemChanged(index)\n    }\n\n    fun notifyChanged(index: Int) {\n        states[index].apply {\n            updating = false\n            updatedAt = System.currentTimeMillis()\n        }\n\n        notifyItemChanged(index)\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterProviderBinding\n                .inflate(context.layoutInflater, parent, false)\n                .also { it.currentTime = currentTime }\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val state = states[position]\n\n        holder.binding.provider = state.provider\n        holder.binding.state = state\n        holder.binding.update = View.OnClickListener {\n            state.updating = true\n\n            requestUpdate(position, state.provider)\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return states.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/ProxyAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.component.ProxyView\nimport yos.clash.material.design.component.ProxyViewConfig\nimport yos.clash.material.design.component.ProxyViewState\n\nclass ProxyAdapter(\n    private val config: ProxyViewConfig,\n    private val clicked: (String) -> Unit,\n) : RecyclerView.Adapter<ProxyAdapter.Holder>() {\n    class Holder(val view: ProxyView) : RecyclerView.ViewHolder(view)\n\n    var selectable: Boolean = false\n    var states: List<ProxyViewState> = emptyList()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(ProxyView(config.context, config))\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = states[position]\n\n        holder.view.apply {\n            state = current\n\n            setOnClickListener {\n                clicked(current.proxy.name)\n            }\n\n            val isSelector = selectable\n\n            isFocusable = isSelector\n            isClickable = isSelector\n\n            current.update(true)\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return states.size\n    }\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/ProxyPageAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport com.github.kr328.clash.core.model.Proxy\nimport yos.clash.material.design.R\nimport yos.clash.material.design.component.ProxyPageFactory\nimport yos.clash.material.design.component.ProxyViewConfig\nimport yos.clash.material.design.component.ProxyViewState\nimport yos.clash.material.design.model.ProxyPageState\nimport yos.clash.material.design.model.ProxyState\nimport yos.clash.material.design.ui.Surface\nimport yos.clash.material.design.util.*\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nclass ProxyPageAdapter(\n    private val surface: Surface,\n    private val config: ProxyViewConfig,\n    private val adapters: List<ProxyAdapter>,\n    private val stateChanged: (Int) -> Unit,\n) : RecyclerView.Adapter<ProxyPageFactory.Holder>() {\n    private val factory = ProxyPageFactory(config)\n    private var parent: RecyclerView? = null\n\n    val states = List(adapters.size) { ProxyPageState() }\n\n    suspend fun updateAdapter(\n        position: Int,\n        proxies: List<Proxy>,\n        selectable: Boolean,\n        parent: ProxyState,\n        links: Map<String, ProxyState>\n    ) {\n        val states = withContext(Dispatchers.Default) {\n            proxies.map {\n                val link = if (it.type.group) links[it.name] else null\n\n                ProxyViewState(config, it, parent, link)\n            }\n        }\n\n        withContext(Dispatchers.Main) {\n            adapters[position].apply {\n                this.selectable = selectable\n                this.swapDataSet(this::states, states, false)\n            }\n\n            requestRedrawVisible()\n        }\n    }\n\n    fun requestRedrawVisible() {\n        factory.fromRoot(parent?.firstVisibleView ?: return)\n            .recyclerView.invalidateChildren()\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProxyPageFactory.Holder {\n        val holder = factory.newInstance()\n\n        val toolbarHeight = config.context.getPixels(R.dimen.toolbar_height)\n        val tabHeight = config.context.getPixels(R.dimen.tab_layout_height)\n\n        holder.recyclerView.bindInsets(surface, toolbarHeight + tabHeight)\n        holder.recyclerView.addScrolledToBottomObserver { view, bottom ->\n            val position = view.position\n            val state = states[position]\n\n            if (state.bottom != bottom) {\n                state.bottom = bottom\n\n                stateChanged(position)\n            }\n        }\n\n        return holder\n    }\n\n    override fun onBindViewHolder(holder: ProxyPageFactory.Holder, position: Int) {\n        val adapter = adapters[position]\n\n        states[position].bottom = false\n\n        holder.recyclerView.apply {\n            this.position = position\n            this.swapAdapter(adapter, false)\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return adapters.size\n    }\n\n    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {\n        this.parent = recyclerView\n\n        recyclerView.isFocusable = false\n    }\n\n    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {\n        this.parent = null\n    }\n\n\n    private var RecyclerView.position: Int\n        get() {\n            return tag as? Int ?: -1\n        }\n        set(value) {\n            tag = value\n        }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/adapter/SideloadProviderAdapter.kt",
    "content": "package yos.clash.material.design.adapter\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.AdapterSideloadProviderBinding\nimport yos.clash.material.design.model.AppInfo\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\nclass SideloadProviderAdapter(\n    private val context: Context,\n    private val apps: List<AppInfo>,\n    var selectedPackageName: String\n) : RecyclerView.Adapter<SideloadProviderAdapter.Holder>() {\n    class Holder(val binding: AdapterSideloadProviderBinding) :\n        RecyclerView.ViewHolder(binding.root)\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {\n        return Holder(\n            AdapterSideloadProviderBinding\n                .inflate(context.layoutInflater, context.root, false)\n        )\n    }\n\n    override fun onBindViewHolder(holder: Holder, position: Int) {\n        val current = apps[position]\n\n        holder.binding.appInfo = current\n\n        holder.binding.selected = selectedPackageName == current.packageName\n\n        holder.binding.root.setOnClickListener {\n            val index = apps.indexOfFirst { it.packageName == selectedPackageName }\n\n            selectedPackageName = current.packageName\n\n            if (index >= 0)\n                notifyItemChanged(index)\n\n            notifyItemChanged(position)\n        }\n    }\n\n    override fun getItemCount(): Int {\n        return apps.size\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/component/AccessControlMenu.kt",
    "content": "package yos.clash.material.design.component\n\nimport android.content.Context\nimport android.view.MenuItem\nimport android.view.View\nimport androidx.appcompat.widget.PopupMenu\nimport yos.clash.material.design.AccessControlDesign.Request\nimport yos.clash.material.design.R\nimport yos.clash.material.design.model.AppInfoSort\nimport yos.clash.material.design.store.UiStore\nimport kotlinx.coroutines.channels.Channel\n\nclass AccessControlMenu(\n    context: Context,\n    menuView: View,\n    private val uiStore: UiStore,\n    private val requests: Channel<Request>,\n) : PopupMenu.OnMenuItemClickListener {\n    private val menu = PopupMenu(context, menuView)\n\n    fun show() {\n        menu.show()\n    }\n\n    override fun onMenuItemClick(item: MenuItem): Boolean {\n        if (item.isCheckable)\n            item.isChecked = !item.isChecked\n\n        when (item.itemId) {\n            R.id.select_all ->\n                requests.trySend(Request.SelectAll)\n            R.id.select_none ->\n                requests.trySend(Request.SelectNone)\n            R.id.select_invert ->\n                requests.trySend(Request.SelectInvert)\n            R.id.system_apps -> {\n                uiStore.accessControlSystemApp = !item.isChecked\n\n                requests.trySend(Request.ReloadApps)\n            }\n            R.id.name -> {\n                uiStore.accessControlSort = AppInfoSort.Label\n\n                requests.trySend(Request.ReloadApps)\n            }\n            R.id.package_name -> {\n                uiStore.accessControlSort = AppInfoSort.PackageName\n\n                requests.trySend(Request.ReloadApps)\n            }\n            R.id.install_time -> {\n                uiStore.accessControlSort = AppInfoSort.InstallTime\n\n                requests.trySend(Request.ReloadApps)\n            }\n            R.id.update_time -> {\n                uiStore.accessControlSort = AppInfoSort.UpdateTime\n\n                requests.trySend(Request.ReloadApps)\n            }\n            R.id.reverse -> {\n                uiStore.accessControlReverse = item.isChecked\n\n                requests.trySend(Request.ReloadApps)\n            }\n            R.id.import_from_clipboard -> {\n                requests.trySend(Request.Import)\n            }\n            R.id.export_to_clipboard -> {\n                requests.trySend(Request.Export)\n            }\n            else -> return false\n        }\n\n        return true\n    }\n\n    init {\n        menu.menuInflater.inflate(R.menu.menu_access_control, menu.menu)\n\n        when (uiStore.accessControlSort) {\n            AppInfoSort.Label ->\n                menu.menu.findItem(R.id.name).isChecked = true\n            AppInfoSort.PackageName ->\n                menu.menu.findItem(R.id.package_name).isChecked = true\n            AppInfoSort.InstallTime ->\n                menu.menu.findItem(R.id.install_time).isChecked = true\n            AppInfoSort.UpdateTime ->\n                menu.menu.findItem(R.id.update_time).isChecked = true\n        }\n\n        menu.menu.findItem(R.id.system_apps).isChecked = !uiStore.accessControlSystemApp\n        menu.menu.findItem(R.id.reverse).isChecked = uiStore.accessControlReverse\n\n        menu.setOnMenuItemClickListener(this)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/component/ProxyMenu.kt",
    "content": "package yos.clash.material.design.component\n\nimport android.content.Context\nimport android.view.MenuItem\nimport android.view.View\nimport androidx.appcompat.widget.PopupMenu\nimport com.github.kr328.clash.core.model.ProxySort\nimport com.github.kr328.clash.core.model.TunnelState\nimport yos.clash.material.design.ProxyDesign\nimport yos.clash.material.design.R\nimport yos.clash.material.design.store.UiStore\nimport kotlinx.coroutines.channels.Channel\nimport yos.clash.material.design.YosConfigAchieve\n\nclass ProxyMenu(\n    context: Context,\n    menuView: View,\n    mode: TunnelState.Mode?,\n    private val uiStore: UiStore,\n    private val requests: Channel<ProxyDesign.Request>,\n    private val updateConfig: () -> Unit,\n) : PopupMenu.OnMenuItemClickListener {\n    private val menu = PopupMenu(context, menuView)\n\n    fun show() {\n        menu.show()\n    }\n\n    override fun onMenuItemClick(item: MenuItem): Boolean {\n        item.isChecked = !item.isChecked\n\n        when (item.itemId) {\n            R.id.not_selectable -> {\n                uiStore.proxyExcludeNotSelectable = item.isChecked\n\n                requests.trySend(ProxyDesign.Request.ReLaunch)\n            }\n            R.id.single -> {\n                uiStore.proxySingleLine = true\n\n                updateConfig()\n\n                requests.trySend(ProxyDesign.Request.ReloadAll)\n            }\n            R.id.multiple -> {\n                uiStore.proxySingleLine = false\n\n                updateConfig()\n\n                requests.trySend(ProxyDesign.Request.ReloadAll)\n            }\n            R.id.default_ -> {\n                uiStore.proxySort = ProxySort.Default\n\n                requests.trySend(ProxyDesign.Request.ReloadAll)\n            }\n            R.id.name -> {\n                uiStore.proxySort = ProxySort.Title\n\n                requests.trySend(ProxyDesign.Request.ReloadAll)\n            }\n            R.id.delay -> {\n                uiStore.proxySort = ProxySort.Delay\n\n                requests.trySend(ProxyDesign.Request.ReloadAll)\n            }\n            R.id.dont_modify -> {\n                requests.trySend(ProxyDesign.Request.PatchMode(null))\n            }\n            R.id.direct_mode -> {\n                requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Direct))\n            }\n            R.id.global_mode -> {\n                requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Global))\n            }\n            R.id.rule_mode -> {\n                requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Rule))\n            }\n            R.id.script_mode -> {\n                requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Script))\n            }\n            else -> return false\n        }\n\n        return true\n    }\n\n    init {\n        menu.menuInflater.inflate(R.menu.menu_proxy, menu.menu)\n\n        menu.menu.apply {\n            findItem(R.id.script_mode).isVisible = YosConfigAchieve.getIfPremium()\n\n            findItem(R.id.not_selectable).isChecked = uiStore.proxyExcludeNotSelectable\n\n            if (uiStore.proxySingleLine) {\n                findItem(R.id.single).isChecked = true\n            } else {\n                findItem(R.id.multiple).isChecked = true\n            }\n\n            when (uiStore.proxySort) {\n                ProxySort.Default -> findItem(R.id.default_).isChecked = true\n                ProxySort.Title -> findItem(R.id.name).isChecked = true\n                ProxySort.Delay -> findItem(R.id.delay).isChecked = true\n            }\n\n            when (mode) {\n                null -> findItem(R.id.dont_modify).isChecked = true\n                TunnelState.Mode.Direct -> findItem(R.id.direct_mode).isChecked = true\n                TunnelState.Mode.Global -> findItem(R.id.global_mode).isChecked = true\n                TunnelState.Mode.Rule -> findItem(R.id.rule_mode).isChecked = true\n                TunnelState.Mode.Script -> findItem(R.id.script_mode).isChecked = true\n            }\n        }\n\n        menu.setOnMenuItemClickListener(this)\n    }\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/component/ProxyPageFactory.kt",
    "content": "package yos.clash.material.design.component\n\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.GridLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.view.VerticalScrollableHost\n\nclass ProxyPageFactory(private val config: ProxyViewConfig) {\n    class Holder(\n        val recyclerView: RecyclerView,\n        val root: View,\n    ) : RecyclerView.ViewHolder(root)\n\n    private val childrenPool = RecyclerView.RecycledViewPool()\n\n    fun newInstance(): Holder {\n        val root = VerticalScrollableHost(config.context).apply {\n            layoutParams = ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT,\n                ViewGroup.LayoutParams.MATCH_PARENT\n            )\n        }\n\n        val recyclerView = RecyclerView(config.context).apply {\n            layoutParams = ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT,\n                ViewGroup.LayoutParams.MATCH_PARENT\n            )\n        }\n\n        root.addView(recyclerView)\n\n        recyclerView.apply {\n            layoutManager = GridLayoutManager(config.context, 2).apply {\n                spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {\n                    override fun getSpanSize(position: Int): Int {\n                        return if (config.singleLine) 2 else 1\n                    }\n                }\n            }\n\n            setRecycledViewPool(childrenPool)\n\n            clipToPadding = false\n        }\n\n        return Holder(recyclerView, root).apply {\n            root.tag = this\n        }\n    }\n\n    fun fromRoot(root: View): Holder {\n        return root.tag!! as Holder\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/component/ProxyView.kt",
    "content": "package yos.clash.material.design.component\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.graphics.Paint\nimport android.graphics.Path\nimport android.view.View\nimport yos.clash.material.common.compat.getDrawableCompat\n\nclass ProxyView(\n    context: Context,\n    config: ProxyViewConfig,\n) : View(context) {\n    constructor(context: Context) : this(context, ProxyViewConfig(context, false))\n\n    init {\n        background = context.getDrawableCompat(config.clickableBackground)\n    }\n\n    var state: ProxyViewState? = null\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val state = state ?: return super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n\n        val width = when (MeasureSpec.getMode(widthMeasureSpec)) {\n            MeasureSpec.UNSPECIFIED ->\n                resources.displayMetrics.widthPixels\n            MeasureSpec.AT_MOST, MeasureSpec.EXACTLY ->\n                MeasureSpec.getSize(widthMeasureSpec)\n            else ->\n                throw IllegalArgumentException(\"invalid measure spec\")\n        }\n\n        state.paint.apply {\n            reset()\n\n            textSize = state.config.textSize\n\n            getTextBounds(\"Stub!\", 0, 1, state.rect)\n        }\n\n        val textHeight = state.rect.height()\n        val exceptHeight = (state.config.layoutPadding * 2 +\n                state.config.contentPadding * 2 +\n                textHeight * 2 +\n                state.config.textMargin).toInt()\n\n        val height = when (MeasureSpec.getMode(heightMeasureSpec)) {\n            MeasureSpec.UNSPECIFIED ->\n                exceptHeight\n            MeasureSpec.AT_MOST, MeasureSpec.EXACTLY ->\n                exceptHeight.coerceAtMost(MeasureSpec.getSize(heightMeasureSpec))\n            else ->\n                throw IllegalArgumentException(\"invalid measure spec\")\n        }\n\n        setMeasuredDimension(width, height)\n    }\n\n    override fun draw(canvas: Canvas) {\n        val state = state ?: return super.draw(canvas)\n\n        if (state.update(false))\n            postInvalidate()\n\n        val width = width.toFloat()\n        val height = height.toFloat()\n\n        val paint = state.paint\n\n        paint.reset()\n\n        paint.color = state.background\n        paint.style = Paint.Style.FILL\n\n        // draw background\n        canvas.apply {\n            if (state.config.singleLine) {\n                drawRect(0f, 0f, width, height, paint)\n            } else {\n                val path = state.path\n\n                path.reset()\n\n                path.addRoundRect(\n                    state.config.layoutPadding,\n                    state.config.layoutPadding,\n                    width - state.config.layoutPadding,\n                    height - state.config.layoutPadding,\n                    state.config.cardRadius,\n                    state.config.cardRadius,\n                    Path.Direction.CW,\n                )\n\n                paint.setShadowLayer(\n                    state.config.cardRadius,\n                    state.config.cardOffset,\n                    state.config.cardOffset,\n                    state.config.shadow\n                )\n\n                drawPath(path, paint)\n\n                clipPath(path)\n            }\n        }\n\n        super.draw(canvas)\n    }\n\n    override fun onDraw(canvas: Canvas) {\n        super.onDraw(canvas)\n\n        val state = state ?: return\n\n        val paint = state.paint\n\n        val width = width.toFloat()\n        val height = height.toFloat()\n\n        paint.textSize = state.config.textSize\n\n        // measure delay text bounds\n        val delayCount = paint.breakText(\n            state.delayText,\n            false,\n            (width - state.config.layoutPadding * 2 - state.config.contentPadding * 2)\n                .coerceAtLeast(0f),\n            null\n        )\n\n        state.paint.getTextBounds(state.delayText, 0, delayCount, state.rect)\n\n        val delayWidth = state.rect.width()\n\n        val mainTextWidth = (width -\n                state.config.layoutPadding * 2 -\n                state.config.contentPadding * 2 -\n                delayWidth -\n                state.config.textMargin * 2\n                )\n            .coerceAtLeast(0f)\n\n        // measure title text bounds\n        val titleCount = paint.breakText(\n            state.title,\n            false,\n            mainTextWidth,\n            null,\n        )\n\n        // measure subtitle text bounds\n        val subtitleCount = paint.breakText(\n            state.subtitle,\n            false,\n            mainTextWidth,\n            null,\n        )\n\n        // text draw measure\n        val textOffset = (paint.descent() + paint.ascent()) / 2\n\n        paint.reset()\n\n        paint.textSize = state.config.textSize\n        paint.isAntiAlias = true\n        paint.color = state.controls\n\n        // draw delay\n        canvas.apply {\n            val x = width - state.config.layoutPadding - state.config.contentPadding - delayWidth\n            val y = height / 2f - textOffset\n\n            drawText(state.delayText, 0, delayCount, x, y, paint)\n        }\n\n        // draw title\n        canvas.apply {\n            val x = state.config.layoutPadding + state.config.contentPadding\n            val y = state.config.layoutPadding +\n                    (height - state.config.layoutPadding * 2) / 3f - textOffset\n\n            drawText(state.title, 0, titleCount, x, y, paint)\n        }\n\n        // draw subtitle\n        canvas.apply {\n            val x = state.config.layoutPadding + state.config.contentPadding\n            val y = state.config.layoutPadding +\n                    (height - state.config.layoutPadding * 2) / 3f * 2 - textOffset\n\n            drawText(state.subtitle, 0, subtitleCount, x, y, paint)\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/component/ProxyViewConfig.kt",
    "content": "package yos.clash.material.design.component\n\nimport android.content.Context\nimport android.graphics.Color\nimport yos.clash.material.design.R\nimport yos.clash.material.design.util.getPixels\nimport yos.clash.material.design.util.resolveThemedColor\nimport yos.clash.material.design.util.resolveThemedResourceId\n\nclass ProxyViewConfig(val context: Context, var singleLine: Boolean) {\n    private val colorSurface = context.resolveThemedColor(R.attr.colorSurface)\n\n    val clickableBackground =\n        context.resolveThemedResourceId(android.R.attr.selectableItemBackground)\n\n    val selectedControl = context.resolveThemedColor(R.attr.colorOnPrimary)\n    val selectedBackground = context.resolveThemedColor(R.attr.colorPrimary)\n\n    val unselectedControl = context.resolveThemedColor(R.attr.colorOnSurface)\n    val unselectedBackground: Int\n        get() = if (singleLine) Color.TRANSPARENT else colorSurface\n\n    val layoutPadding = context.getPixels(R.dimen.proxy_layout_padding).toFloat()\n    val contentPadding = context.getPixels(R.dimen.proxy_content_padding).toFloat()\n    val textMargin = context.getPixels(R.dimen.proxy_text_margin)\n    val textSize = context.getPixels(R.dimen.proxy_text_size).toFloat()\n\n    val shadow = Color.argb(\n        0x15,\n        Color.red(Color.DKGRAY),\n        Color.green(Color.DKGRAY),\n        Color.blue(Color.DKGRAY),\n    )\n\n    val cardRadius = context.getPixels(R.dimen.proxy_card_radius).toFloat()\n    var cardOffset = context.getPixels(R.dimen.proxy_card_offset).toFloat()\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/component/ProxyViewState.kt",
    "content": "package yos.clash.material.design.component\n\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.graphics.Path\nimport android.graphics.Rect\nimport com.github.kr328.clash.core.model.Proxy\nimport yos.clash.material.design.model.ProxyState\nimport kotlin.math.absoluteValue\nimport kotlin.math.max\n\nclass ProxyViewState(\n    val config: ProxyViewConfig,\n    val proxy: Proxy,\n    private val parent: ProxyState,\n    private val link: ProxyState?\n) {\n    val paint = Paint()\n    val rect = Rect()\n    val path = Path()\n\n    var title: String = \"\"\n    var subtitle: String = \"\"\n    var delayText: String = \"\"\n    var background: Int = config.unselectedBackground\n    var controls: Int = config.unselectedControl\n\n    private var delay: Int = 0\n    private var selected: Boolean = false\n    private var parentNow: String = \"\"\n    private var linkNow: String? = null\n\n    private var lastFrameTime = System.currentTimeMillis()\n\n    fun update(snap: Boolean): Boolean {\n        val frameTime = System.currentTimeMillis()\n        var invalidate = false\n\n        if (proxy.type.group) {\n            title = proxy.name\n\n            if (link == null) {\n                subtitle = proxy.type.name\n            } else {\n                if (linkNow !== link.now) {\n                    linkNow = link.now\n\n                    subtitle = \"%s(%s)\".format(\n                        proxy.type.name,\n                        link.now.ifEmpty { \"*\" }\n                    )\n                }\n            }\n        } else {\n            title = proxy.title\n            subtitle = proxy.subtitle\n        }\n\n        if (delay != proxy.delay) {\n            delay = proxy.delay\n            delayText = if (proxy.delay in 0..Short.MAX_VALUE) proxy.delay.toString() else \"\"\n        }\n\n        if (parentNow !== parent.now) {\n            parentNow = parent.now\n            selected = proxy.name == parent.now\n        }\n\n        controls = if (selected) config.selectedControl else config.unselectedControl\n\n        if (snap) {\n            background = if (selected) config.selectedBackground else config.unselectedBackground\n        } else {\n            val target = if (selected) config.selectedBackground else config.unselectedBackground\n\n            if (background != target) {\n                val sa = Color.alpha(background)\n                val sr = Color.red(background)\n                val sg = Color.green(background)\n                val sb = Color.blue(background)\n\n                val ta = Color.alpha(target)\n                val tr = Color.red(target)\n                val tg = Color.green(target)\n                val tb = Color.blue(target)\n\n                val da = ta - sa\n                val dr = tr - sr\n                val dg = tg - sg\n                val db = tb - sb\n\n                val max = max(\n                    da.absoluteValue,\n                    max(\n                        dr.absoluteValue,\n                        max(\n                            dg.absoluteValue,\n                            db.absoluteValue\n                        )\n                    )\n                )\n\n                val frameOffset = frameTime - lastFrameTime\n\n                val colorOffset = (frameOffset / max.toFloat().coerceAtLeast(0.001f))\n                    .coerceIn(0.0f, 1.0f)\n\n                background = if (colorOffset > 0.999f) {\n                    target\n                } else {\n                    Color.argb(\n                        (sa + da * colorOffset).toInt(),\n                        (sr + dr * colorOffset).toInt(),\n                        (sg + dg * colorOffset).toInt(),\n                        (sb + db * colorOffset).toInt()\n                    )\n                }\n\n                invalidate = true\n            }\n        }\n\n        lastFrameTime = frameTime\n\n        return invalidate\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/dialog/Dialogs.kt",
    "content": "package yos.clash.material.design.dialog\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.os.Bundle\nimport android.view.ViewGroup\nimport android.view.WindowManager\nimport android.widget.FrameLayout\nimport androidx.coordinatorlayout.widget.CoordinatorLayout\nimport androidx.core.view.ViewCompat\nimport yos.clash.material.common.compat.isAllowForceDarkCompat\nimport yos.clash.material.common.compat.isSystemBarsTranslucentCompat\nimport yos.clash.material.design.R\nimport yos.clash.material.design.ui.Insets\nimport yos.clash.material.design.ui.Surface\nimport yos.clash.material.design.util.getPixels\nimport yos.clash.material.design.util.resolveThemedResourceId\nimport yos.clash.material.design.util.setOnInsertsChangedListener\nimport com.google.android.material.bottomsheet.BottomSheetBehavior\nimport com.google.android.material.bottomsheet.BottomSheetDialog\n\nclass AppBottomSheetDialog(context: Context) : BottomSheetDialog(context) {\n    private var insets: Insets = Insets.EMPTY\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        setCancelable(true)\n\n        window!!.apply {\n            isSystemBarsTranslucentCompat = true\n            isAllowForceDarkCompat = false\n        }\n\n        findViewById<ViewGroup>(com.google.android.material.R.id.container)?.apply {\n            fitsSystemWindows = false\n        }\n\n        findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)?.apply {\n            setOnInsertsChangedListener {\n                if (insets != it) {\n                    insets = it\n\n                    (layoutParams as CoordinatorLayout.LayoutParams).also { params ->\n                        if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR) {\n                            params.setMargins(it.start, 0, it.end, 0)\n                        } else {\n                            params.setMargins(it.end, 0, it.start, 0)\n                        }\n\n                        val top = context.getPixels(R.dimen.bottom_sheet_background_padding_top)\n                        val height = context.getPixels(R.dimen.bottom_sheet_header_height)\n\n                        setPaddingRelative(\n                            0,\n                            top * 2 + height,\n                            0,\n                            it.bottom\n                        )\n                    }\n                }\n            }\n        }\n\n        setOnShowListener {\n            behavior.halfExpandedRatio = 0.99f\n            behavior.state = BottomSheetBehavior.STATE_EXPANDED\n        }\n    }\n}\n\nclass FullScreenDialog(\n    context: Context\n) : Dialog(context, context.resolveThemedResourceId(R.attr.fullScreenDialogTheme)) {\n    val surface = Surface()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        window!!.apply {\n            isSystemBarsTranslucentCompat = true\n\n            setLayout(\n                WindowManager.LayoutParams.MATCH_PARENT,\n                WindowManager.LayoutParams.MATCH_PARENT\n            )\n\n            decorView.setOnInsertsChangedListener {\n                if (surface.insets != it)\n                    surface.insets = it\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/dialog/Input.kt",
    "content": "package yos.clash.material.design.dialog\n\nimport android.content.Context\nimport androidx.appcompat.app.AlertDialog\nimport androidx.core.widget.doOnTextChanged\nimport yos.clash.material.design.R\nimport yos.clash.material.design.databinding.DialogTextFieldBinding\nimport yos.clash.material.design.util.*\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlin.coroutines.resume\n\nsuspend fun Context.requestModelTextInput(\n    initial: String,\n    title: CharSequence,\n    hint: CharSequence? = null,\n    error: CharSequence? = null,\n    validator: Validator = ValidatorAcceptAll,\n): String {\n    return this.requestModelTextInput(initial, title, null, hint, error, validator)!!\n}\n\nsuspend fun Context.requestModelTextInput(\n    initial: String?,\n    title: CharSequence,\n    reset: CharSequence?,\n    hint: CharSequence? = null,\n    error: CharSequence? = null,\n    validator: Validator = ValidatorAcceptAll,\n): String? {\n    return suspendCancellableCoroutine {\n        val binding = DialogTextFieldBinding\n            .inflate(layoutInflater, this.root, false)\n\n        val builder = MaterialAlertDialogBuilder(this)\n            .setTitle(title)\n            .setView(binding.root)\n            .setCancelable(true)\n            .setPositiveButton(R.string.ok) { _, _ ->\n                val text = binding.textField.text?.toString() ?: \"\"\n\n                if (validator(text))\n                    it.resume(text)\n                else\n                    it.resume(initial)\n            }\n            .setNegativeButton(R.string.cancel) { _, _ -> }\n            .setOnDismissListener { _ ->\n                if (!it.isCompleted)\n                    it.resume(initial)\n            }\n\n        if (reset != null) {\n            builder.setNeutralButton(reset) { _, _ ->\n                it.resume(null)\n            }\n        }\n\n        val dialog = builder.create()\n\n        it.invokeOnCancellation {\n            dialog.dismiss()\n        }\n\n        dialog.setOnShowListener {\n            if (hint != null)\n                binding.textLayout.hint = hint\n\n            binding.textField.apply {\n                binding.textLayout.isErrorEnabled = error != null\n\n                doOnTextChanged { text, _, _, _ ->\n                    if (!validator(text?.toString() ?: \"\")) {\n                        if (error != null)\n                            binding.textLayout.error = error\n\n                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false\n                    } else {\n                        if (error != null)\n                            binding.textLayout.error = null\n\n                        dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true\n                    }\n                }\n\n                setText(initial)\n\n                setSelection(0, initial?.length ?: 0)\n\n                requestTextInput()\n            }\n        }\n\n        dialog.show()\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/dialog/Progress.kt",
    "content": "package yos.clash.material.design.dialog\n\nimport android.content.Context\nimport yos.clash.material.design.databinding.DialogFetchStatusBinding\nimport yos.clash.material.design.util.layoutInflater\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\ninterface ModelProgressBarConfigure {\n    var isIndeterminate: Boolean\n    var text: String?\n    var progress: Int\n    var max: Int\n}\n\ninterface ModelProgressBarScope {\n    suspend fun configure(block: suspend ModelProgressBarConfigure.() -> Unit)\n}\n\nsuspend fun Context.withModelProgressBar(block: suspend ModelProgressBarScope.() -> Unit) {\n    val view = DialogFetchStatusBinding.inflate(this.layoutInflater)\n    val dialog = MaterialAlertDialogBuilder(this)\n        .setCancelable(false)\n        .setView(view.root)\n        .show()\n\n    val configureImpl = object : ModelProgressBarConfigure {\n        override var isIndeterminate: Boolean\n            get() = view.progressIndicator.isIndeterminate\n            set(value) {\n                view.progressIndicator.isIndeterminate = value\n            }\n        override var text: String?\n            get() = view.text.text?.toString()\n            set(value) {\n                view.text.text = value\n            }\n        override var progress: Int\n            get() = view.progressIndicator.progress\n            set(value) {\n                view.progressIndicator.setProgressCompat(value, true)\n            }\n        override var max: Int\n            get() = view.progressIndicator.max\n            set(value) {\n                view.progressIndicator.max = value\n            }\n\n    }\n\n    val scopeImpl = object : ModelProgressBarScope {\n        override suspend fun configure(block: suspend ModelProgressBarConfigure.() -> Unit) {\n            withContext(Dispatchers.Main) {\n                configureImpl.block()\n            }\n        }\n    }\n\n    try {\n        scopeImpl.block()\n    } finally {\n        dialog.dismiss()\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/AppInfo.kt",
    "content": "package yos.clash.material.design.model\n\nimport android.graphics.drawable.Drawable\n\ndata class AppInfo(\n    val packageName: String,\n    val label: String,\n    val icon: Drawable,\n    val installTime: Long,\n    val updateDate: Long,\n)"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/AppInfoSort.kt",
    "content": "package yos.clash.material.design.model\n\nenum class AppInfoSort(comparator: Comparator<AppInfo>) : Comparator<AppInfo> by comparator {\n    Label(compareBy(AppInfo::label)),\n    PackageName(compareBy(AppInfo::packageName)),\n    InstallTime(compareBy(AppInfo::installTime)),\n    UpdateTime(compareBy(AppInfo::updateDate)),\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/Behavior.kt",
    "content": "package yos.clash.material.design.model\n\ninterface Behavior {\n    var autoRestart: Boolean\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/DarkMode.kt",
    "content": "package yos.clash.material.design.model\n\nenum class DarkMode {\n    Auto, ForceLight, ForceDark\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/File.kt",
    "content": "package yos.clash.material.design.model\n\ndata class File(\n    val id: String,\n    val name: String,\n    val size: Long,\n    val lastModified: Long,\n    val isDirectory: Boolean\n)"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/LogFile.kt",
    "content": "package yos.clash.material.design.model\n\nimport java.util.*\n\ndata class LogFile(val fileName: String, val date: Date) {\n    companion object {\n        private val REGEX_FILE = Regex(\"clash-(\\\\d+).log\")\n        private const val FORMAT_FILE_NAME = \"clash-%d.log\"\n\n        fun parseFromFileName(fileName: String): LogFile? {\n            return REGEX_FILE.matchEntire(fileName)?.run {\n                LogFile(fileName, Date(groupValues[1].toLong()))\n            }\n        }\n\n        fun generate(): LogFile {\n            val current = Date()\n            val fileName = FORMAT_FILE_NAME.format(current.time)\n\n            return LogFile(fileName, current)\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/ProfileProvider.kt",
    "content": "package yos.clash.material.design.model\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.drawable.Drawable\nimport yos.clash.material.common.compat.getDrawableCompat\nimport yos.clash.material.design.R\n\nsealed class ProfileProvider {\n    class File(private val context: Context) : ProfileProvider() {\n        override val name: String\n            get() = context.getString(R.string.file)\n        override val summary: String\n            get() = context.getString(R.string.import_from_file)\n        override val icon: Drawable?\n            get() = context.getDrawableCompat(R.drawable.ic_baseline_attach_file)\n    }\n\n    class Url(private val context: Context) : ProfileProvider() {\n        override val name: String\n            get() = context.getString(R.string.url)\n        override val summary: String\n            get() = context.getString(R.string.import_from_url)\n        override val icon: Drawable?\n            get() = context.getDrawableCompat(R.drawable.ic_baseline_cloud_download)\n    }\n\n    class External(\n        override val name: String,\n        override val summary: String,\n        override val icon: Drawable?,\n        val intent: Intent,\n    ) : ProfileProvider()\n\n    abstract val name: String\n    abstract val summary: String\n    abstract val icon: Drawable?\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/ProviderState.kt",
    "content": "package yos.clash.material.design.model\n\nimport androidx.databinding.BaseObservable\nimport androidx.databinding.Bindable\nimport com.github.kr328.clash.core.model.Provider\nimport yos.clash.material.design.BR\n\nclass ProviderState(\n    val provider: Provider,\n    updatedAt: Long,\n    updating: Boolean,\n) : BaseObservable() {\n    var updatedAt: Long = updatedAt\n        @Bindable get\n        set(value) {\n            field = value\n\n            notifyPropertyChanged(BR.updatedAt)\n        }\n\n    var updating: Boolean = updating\n        @Bindable get\n        set(value) {\n            field = value\n\n            notifyPropertyChanged(BR.updating)\n        }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/ProxyPageState.kt",
    "content": "package yos.clash.material.design.model\n\nclass ProxyPageState {\n    var bottom = false\n    var urlTesting = false\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/model/ProxyState.kt",
    "content": "package yos.clash.material.design.model\n\ndata class ProxyState(var now: String)"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Category.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.view.View\nimport androidx.annotation.StringRes\nimport yos.clash.material.design.databinding.PreferenceCategoryBinding\nimport yos.clash.material.design.util.layoutInflater\n\nfun PreferenceScreen.category(\n    @StringRes text: Int,\n) {\n    val binding = PreferenceCategoryBinding\n        .inflate(context.layoutInflater, root, false)\n\n    binding.textView.text = context.getString(text)\n\n    addElement(object : Preference {\n        override val view: View\n            get() = binding.root\n        override var enabled: Boolean\n            get() = binding.root.isEnabled\n            set(value) {\n                binding.root.isEnabled = value\n            }\n    })\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Clickable.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.graphics.drawable.Drawable\nimport android.view.View\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\nimport yos.clash.material.common.compat.getDrawableCompat\nimport yos.clash.material.design.databinding.PreferenceClickableBinding\nimport yos.clash.material.design.util.layoutInflater\n\ninterface ClickablePreference : Preference {\n    var title: CharSequence\n\n    var icon: Drawable?\n    var summary: CharSequence?\n\n    fun clicked(clicked: () -> Unit)\n}\n\nfun PreferenceScreen.clickable(\n    @StringRes title: Int,\n    @DrawableRes icon: Int? = null,\n    @StringRes summary: Int? = null,\n    configure: ClickablePreference.() -> Unit = {}\n): ClickablePreference {\n    val binding = PreferenceClickableBinding\n        .inflate(context.layoutInflater, root, false)\n\n    val impl = object : ClickablePreference {\n        override var icon: Drawable?\n            get() = binding.iconView.background\n            set(value) {\n                binding.iconView.background = value\n                binding.iconView.visibility = if (value == null) View.GONE else View.VISIBLE\n            }\n        override var title: CharSequence\n            get() = binding.titleView.text\n            set(value) {\n                binding.titleView.text = value\n            }\n        override var summary: CharSequence?\n            get() = binding.summaryView.text\n            set(value) {\n                binding.summaryView.text = value\n                binding.summaryView.visibility = if (value == null) View.GONE else View.VISIBLE\n            }\n        override val view: View\n            get() = binding.root\n\n        override fun clicked(clicked: () -> Unit) {\n            binding.root.setOnClickListener {\n                clicked()\n            }\n        }\n    }\n\n    impl.title = context.getText(title)\n\n    if (icon != null) {\n        impl.icon = context.getDrawableCompat(icon)\n    } else {\n        impl.icon = null\n    }\n\n    if (summary != null) {\n        impl.summary = context.getText(summary)\n    } else {\n        impl.summary = null\n    }\n\n    impl.configure()\n\n    addElement(impl)\n\n    return impl\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/EditableText.kt",
    "content": "package yos.clash.material.design.preference\n\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.dialog.requestModelTextInput\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport kotlin.reflect.KMutableProperty0\n\ninterface EditableTextPreference : ClickablePreference {\n    var placeholder: CharSequence?\n    var empty: CharSequence?\n    var text: String?\n}\n\nfun <T> PreferenceScreen.editableText(\n    value: KMutableProperty0<T>,\n    adapter: NullableTextAdapter<T>,\n    @StringRes title: Int,\n    @DrawableRes icon: Int? = null,\n    @StringRes placeholder: Int? = null,\n    @StringRes empty: Int? = null,\n    configure: EditableTextPreference.() -> Unit = {},\n): EditableTextPreference {\n    val impl = object : EditableTextPreference, ClickablePreference by clickable(title, icon) {\n        override var placeholder: CharSequence? = null\n        override var empty: CharSequence? = null\n        override var text: String? = null\n            set(value) {\n                field = value\n\n                when {\n                    value == null -> {\n                        this.summary = this.placeholder\n                    }\n                    value.isEmpty() -> {\n                        this.summary = this.empty\n                    }\n                    else -> {\n                        this.summary = value\n                    }\n                }\n            }\n    }\n\n    if (placeholder != null) {\n        impl.placeholder = context.getText(placeholder)\n    }\n\n    if (empty != null) {\n        impl.empty = context.getText(empty)\n    }\n\n    impl.configure()\n\n    launch(Dispatchers.Main) {\n        impl.text = withContext(Dispatchers.IO) {\n            adapter.from(value.get())\n        }\n\n        impl.clicked {\n            this@editableText.launch(Dispatchers.Main) {\n                val text = context.requestModelTextInput(\n                    initial = impl.text,\n                    title = impl.title,\n                    reset = context.getText(R.string.reset),\n                    hint = impl.title,\n                )\n\n                val newValue = withContext(Dispatchers.IO) {\n                    adapter.to(text).apply(value::set)\n                }\n\n                impl.text = adapter.from(newValue)\n            }\n        }\n    }\n\n    return impl\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/EditableTextList.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.content.Context\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.adapter.EditableTextListAdapter\nimport yos.clash.material.design.dialog.requestModelTextInput\nimport kotlinx.coroutines.*\nimport kotlin.reflect.KMutableProperty0\n\ninterface EditableTextListPreference<T> : ClickablePreference {\n    var placeholder: CharSequence?\n\n    var list: List<T>?\n}\n\nfun <T> PreferenceScreen.editableTextList(\n    value: KMutableProperty0<List<T>?>,\n    adapter: TextAdapter<T>,\n    @StringRes title: Int,\n    @DrawableRes icon: Int? = null,\n    @StringRes placeholder: Int? = null,\n    configure: EditableTextListPreference<T>.() -> Unit = {},\n): EditableTextListPreference<T> {\n    val impl =\n        object : EditableTextListPreference<T>, ClickablePreference by clickable(title, icon) {\n            override var list: List<T>? = null\n                set(value) {\n                    field = value\n\n                    when {\n                        value == null -> {\n                            this.summary = this.placeholder\n                        }\n                        value.isEmpty() -> {\n                            this.summary = context.getString(R.string.empty)\n                        }\n                        else -> {\n                            this.summary = context.getString(R.string.format_elements, value.size)\n                        }\n                    }\n                }\n            override var placeholder: CharSequence? = null\n        }\n\n    if (placeholder != null) {\n        impl.placeholder = context.getText(placeholder)\n    }\n\n    impl.configure()\n\n    launch(Dispatchers.Main) {\n        val v = withContext(Dispatchers.IO) {\n            value.get()\n        }\n\n        impl.list = v\n\n        impl.clicked {\n            this@editableTextList.launch(Dispatchers.Main) {\n                val newList = requestEditTextList(\n                    impl.list,\n                    context,\n                    adapter,\n                    impl.title\n                )\n\n                withContext(Dispatchers.IO) {\n                    value.set(newList)\n                }\n\n                impl.list = newList\n            }\n        }\n    }\n\n    return impl\n}\n\nprivate suspend fun <T> requestEditTextList(\n    initialValue: List<T>?,\n    context: Context,\n    adapter: TextAdapter<T>,\n    title: CharSequence,\n): List<T>? {\n    val recyclerAdapter = EditableTextListAdapter(\n        context,\n        initialValue?.toMutableList() ?: mutableListOf(),\n        adapter\n    )\n\n    val result = requestEditableListOverlay(context, recyclerAdapter, title) {\n        val text = context.requestModelTextInput(\n            initial = \"\",\n            title = title,\n            hint = title\n        )\n\n        if (text.isNotBlank()) {\n            recyclerAdapter.addElement(text)\n        }\n    }\n\n    return when (result) {\n        EditableListOverlayResult.Cancel -> initialValue\n        EditableListOverlayResult.Apply -> recyclerAdapter.values\n        EditableListOverlayResult.Reset -> null\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/EditableTextMap.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.content.Context\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.adapter.EditableTextMapAdapter\nimport yos.clash.material.design.databinding.DialogEditableMapTextFieldBinding\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.*\nimport kotlin.coroutines.resume\nimport kotlin.reflect.KMutableProperty0\n\ninterface EditableTextMapPreference<K, V> : ClickablePreference {\n    var placeholder: CharSequence?\n    var map: Map<K, V>?\n}\n\nfun <K, V> PreferenceScreen.editableTextMap(\n    value: KMutableProperty0<Map<K, V>?>,\n    keyAdapter: TextAdapter<K>,\n    valueAdapter: TextAdapter<V>,\n    @StringRes title: Int,\n    @DrawableRes icon: Int? = null,\n    @StringRes placeholder: Int? = null,\n    configure: EditableTextMapPreference<K, V>.() -> Unit = {}\n): EditableTextMapPreference<K, V> {\n    val impl =\n        object : EditableTextMapPreference<K, V>, ClickablePreference by clickable(title, icon) {\n            override var placeholder: CharSequence? = null\n            override var map: Map<K, V>? = null\n                set(value) {\n                    field = value\n\n                    when {\n                        value == null -> {\n                            this.summary = this.placeholder\n                        }\n                        value.isEmpty() -> {\n                            this.summary = context.getString(R.string.empty)\n                        }\n                        else -> {\n                            this.summary = context.getString(R.string.format_elements, value.size)\n                        }\n                    }\n                }\n        }\n\n    if (placeholder != null) {\n        impl.placeholder = context.getText(placeholder)\n    }\n\n    impl.configure()\n\n    launch(Dispatchers.Main) {\n        val v = withContext(Dispatchers.IO) {\n            value.get()\n        }\n\n        impl.map = v\n\n        impl.clicked {\n            this@editableTextMap.launch(Dispatchers.Main) {\n                val newMap = requestEditTextMap(\n                    impl.map,\n                    context,\n                    keyAdapter,\n                    valueAdapter,\n                    impl.title\n                )\n\n                withContext(Dispatchers.IO) {\n                    value.set(newMap)\n                }\n\n                impl.map = newMap\n            }\n        }\n    }\n\n    return impl\n}\n\nprivate suspend fun <K, V> requestEditTextMap(\n    initialValue: Map<K, V>?,\n    context: Context,\n    keyAdapter: TextAdapter<K>,\n    valueAdapter: TextAdapter<V>,\n    title: CharSequence\n): Map<K, V>? {\n    val editableValue = withContext(Dispatchers.Default) {\n        initialValue?.map { it.key to it.value }?.toMutableList() ?: mutableListOf()\n    }\n\n    val recyclerAdapter = EditableTextMapAdapter(\n        context,\n        editableValue,\n        keyAdapter,\n        valueAdapter,\n    )\n\n    val result = requestEditableListOverlay(context, recyclerAdapter, title) {\n        val newItem = requestModelInputEntry(context, title)\n\n        if (newItem != null) {\n            recyclerAdapter.addElement(newItem.first, newItem.second)\n        }\n    }\n\n    return when (result) {\n        EditableListOverlayResult.Cancel -> initialValue\n        EditableListOverlayResult.Apply -> recyclerAdapter.values.toMap()\n        EditableListOverlayResult.Reset -> null\n    }\n}\n\nprivate suspend fun requestModelInputEntry(\n    context: Context,\n    title: CharSequence\n): Pair<String, String>? {\n    return suspendCancellableCoroutine { ctx ->\n        val binding = DialogEditableMapTextFieldBinding\n            .inflate(context.layoutInflater, context.root, false)\n\n        val dialog = MaterialAlertDialogBuilder(context)\n            .setTitle(title)\n            .setNegativeButton(R.string.cancel) { _, _ -> }\n            .setPositiveButton(R.string.ok) { _, _ ->\n                val k = binding.keyView.text?.toString()?.trim() ?: \"\"\n                val v = binding.valueView.text?.toString()?.trim() ?: \"\"\n\n                if (k.isNotEmpty() && v.isNotEmpty()) {\n                    ctx.resume(k to v)\n                }\n            }\n            .setView(binding.root)\n            .create()\n\n        dialog.setOnCancelListener {\n            if (!ctx.isCompleted) {\n                ctx.resume(null)\n            }\n        }\n\n        dialog.show()\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Overlay.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.content.Context\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.databinding.DialogPreferenceListBinding\nimport yos.clash.material.design.dialog.FullScreenDialog\nimport yos.clash.material.design.util.applyLinearAdapter\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlin.coroutines.resume\n\ninternal enum class EditableListOverlayResult {\n    Cancel, Apply, Reset\n}\n\ninternal suspend fun requestEditableListOverlay(\n    context: Context,\n    adapter: RecyclerView.Adapter<*>,\n    title: CharSequence,\n    addNewItem: suspend () -> Unit\n): EditableListOverlayResult {\n    return coroutineScope {\n        suspendCancellableCoroutine { ctx ->\n            val dialog = FullScreenDialog(context)\n            val binding = DialogPreferenceListBinding\n                .inflate(context.layoutInflater, context.root, false)\n\n            binding.surface = dialog.surface\n            binding.mainList.applyLinearAdapter(context, adapter)\n            binding.titleView.text = title\n\n            binding.newView.setOnClickListener {\n                launch {\n                    addNewItem()\n                }\n            }\n\n            binding.resetView.setOnClickListener {\n                ctx.resume(EditableListOverlayResult.Reset)\n\n                dialog.dismiss()\n            }\n\n            binding.cancelView.setOnClickListener {\n                dialog.dismiss()\n            }\n\n            binding.okView.setOnClickListener {\n                ctx.resume(EditableListOverlayResult.Apply)\n\n                dialog.dismiss()\n            }\n\n            dialog.setContentView(binding.root)\n\n            dialog.setOnDismissListener {\n                if (!ctx.isCompleted) {\n                    ctx.resume(EditableListOverlayResult.Cancel)\n                }\n            }\n\n            ctx.invokeOnCancellation {\n                dialog.dismiss()\n            }\n\n            dialog.show()\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Preference.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.view.View\n\nfun interface OnChangedListener {\n    fun onChanged()\n}\n\ninterface Preference {\n    val view: View\n\n    var enabled: Boolean\n        get() = view.isEnabled\n        set(value) {\n            view.isEnabled = value\n            view.isClickable = value\n            view.isFocusable = value\n            view.alpha = if (value) 1.0f else 0.33f\n        }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Screen.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.content.Context\nimport android.view.ViewGroup\nimport android.widget.LinearLayout\nimport android.widget.LinearLayout.LayoutParams\nimport android.widget.LinearLayout.LayoutParams.MATCH_PARENT\nimport android.widget.LinearLayout.LayoutParams.WRAP_CONTENT\nimport kotlinx.coroutines.CoroutineScope\n\ninterface PreferenceScreen : CoroutineScope {\n    val context: Context\n    val root: ViewGroup\n}\n\nfun CoroutineScope.preferenceScreen(\n    context: Context,\n    configure: PreferenceScreen.() -> Unit\n): PreferenceScreen {\n    val root = LinearLayout(context).apply {\n        orientation = LinearLayout.VERTICAL\n    }\n\n    val impl = object : PreferenceScreen, CoroutineScope by this {\n        override val context: Context\n            get() = context\n        override val root: ViewGroup\n            get() = root\n    }\n\n    impl.configure()\n\n    return impl\n}\n\nfun PreferenceScreen.addElement(preference: Preference) {\n    root.addView(preference.view, LayoutParams(MATCH_PARENT, WRAP_CONTENT))\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/SelectableList.kt",
    "content": "package yos.clash.material.design.preference\n\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\nimport androidx.appcompat.widget.ListPopupWindow\nimport yos.clash.material.design.R\nimport yos.clash.material.design.adapter.PopupListAdapter\nimport yos.clash.material.design.util.getPixels\nimport yos.clash.material.design.util.measureWidth\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport kotlin.reflect.KMutableProperty0\n\ninterface SelectableListPreference<T> : ClickablePreference {\n    var selected: Int\n\n    var listener: OnChangedListener?\n}\n\nfun <T> PreferenceScreen.selectableList(\n    value: KMutableProperty0<T>,\n    values: Array<T>,\n    valuesText: Array<Int>,\n    @StringRes title: Int,\n    @DrawableRes icon: Int? = null,\n    configure: SelectableListPreference<T>.() -> Unit = {},\n): SelectableListPreference<T> {\n    val impl = object : SelectableListPreference<T>, ClickablePreference by clickable(title, icon) {\n        override var selected: Int = 0\n            set(value) {\n                field = value\n\n                this.summary = context.getText(valuesText[value])\n            }\n        override var listener: OnChangedListener? = null\n    }\n\n    impl.configure()\n\n    launch(Dispatchers.Main) {\n        val initial = withContext(Dispatchers.IO) {\n            value.get()\n        }\n\n        impl.selected = values.indexOf(initial)\n\n        impl.clicked {\n            popupSelectMenu(impl, value, valuesText.map { context.getText(it) }, values)\n        }\n    }\n\n    return impl\n}\n\nprivate fun <T> PreferenceScreen.popupSelectMenu(\n    impl: SelectableListPreference<T>,\n    value: KMutableProperty0<T>,\n    valuesText: List<CharSequence>,\n    values: Array<T>,\n) {\n    ListPopupWindow(context).apply {\n        val adapter = PopupListAdapter(\n            context,\n            valuesText,\n            impl.selected,\n        )\n\n        setAdapter(adapter)\n\n        anchorView = impl.view\n\n        width = adapter.measureWidth(context)\n            .coerceAtLeast(context.getPixels(R.dimen.dialog_menu_min_width))\n\n        isModal = true\n\n        horizontalOffset = context.getPixels(R.dimen.item_header_component_size) +\n                context.getPixels(R.dimen.item_header_margin) * 2\n\n        setOnItemClickListener { _, _, position, _ ->\n            dismiss()\n\n            launch(Dispatchers.Main) {\n                withContext(Dispatchers.IO) {\n                    value.set(values[position])\n                }\n\n                impl.selected = position\n                impl.listener?.onChanged()\n            }\n        }\n\n        show()\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Switch.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.graphics.drawable.Drawable\nimport android.view.View\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\nimport yos.clash.material.common.compat.getDrawableCompat\nimport yos.clash.material.design.databinding.PreferenceSwitchBinding\nimport yos.clash.material.design.util.layoutInflater\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport kotlin.reflect.KMutableProperty0\n\ninterface SwitchPreference : Preference {\n    var icon: Drawable?\n    var title: CharSequence?\n    var summary: CharSequence?\n    var listener: OnChangedListener?\n}\n\nfun PreferenceScreen.switch(\n    value: KMutableProperty0<Boolean>,\n    @DrawableRes icon: Int? = null,\n    @StringRes title: Int? = null,\n    @StringRes summary: Int? = null,\n    configure: SwitchPreference.() -> Unit = {},\n): SwitchPreference {\n    val binding = PreferenceSwitchBinding\n        .inflate(context.layoutInflater, root, false)\n\n    val impl = object : SwitchPreference {\n        override val view: View\n            get() = binding.root\n        override var icon: Drawable?\n            get() = binding.iconView.background\n            set(value) {\n                binding.iconView.background = value\n                binding.iconView.visibility = if (value == null) View.GONE else View.VISIBLE\n            }\n        override var title: CharSequence?\n            get() = binding.titleView.text\n            set(value) {\n                binding.titleView.text = value\n            }\n        override var summary: CharSequence?\n            get() = binding.summaryView.text\n            set(value) {\n                binding.summaryView.text = value\n            }\n        override var listener: OnChangedListener? = null\n        override var enabled: Boolean\n            get() = binding.root.isEnabled\n            set(value) {\n                binding.root.isEnabled = value\n                binding.root.isFocusable = value\n                binding.root.isClickable = value\n                binding.root.alpha = if (value) 1.0f else 0.33f\n            }\n\n    }\n\n    if (icon != null) {\n        impl.icon = context.getDrawableCompat(icon)\n    } else {\n        impl.icon = null\n    }\n\n    if (title != null) {\n        impl.title = context.getString(title)\n    }\n\n    if (summary != null) {\n        impl.summary = context.getString(summary)\n    }\n\n    impl.configure()\n\n    addElement(impl)\n\n    launch(Dispatchers.Main) {\n        val initialValue = withContext(Dispatchers.IO) {\n            value.get()\n        }\n\n        binding.switchView.apply {\n            isChecked = initialValue\n\n            binding.root.setOnClickListener {\n                isChecked = !isChecked\n\n                this@switch.launch(Dispatchers.Main) {\n                    withContext(Dispatchers.IO) {\n                        value.set(isChecked)\n                    }\n\n                    impl.listener?.onChanged()\n                }\n            }\n        }\n    }\n\n    return impl\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Tips.kt",
    "content": "package yos.clash.material.design.preference\n\nimport android.view.View\nimport androidx.annotation.StringRes\nimport yos.clash.material.design.databinding.PreferenceTipsBinding\nimport yos.clash.material.design.util.getHtml\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.root\n\ninterface TipsPreference : Preference {\n    var text: CharSequence?\n}\n\nfun PreferenceScreen.tips(\n    @StringRes text: Int,\n    configure: TipsPreference.() -> Unit = {},\n): TipsPreference {\n    val binding = PreferenceTipsBinding\n        .inflate(context.layoutInflater, context.root, false)\n    val impl = object : TipsPreference {\n        override var text: CharSequence?\n            get() = binding.tips.text\n            set(value) {\n                binding.tips.text = value\n            }\n        override val view: View\n            get() = binding.root\n        override var enabled: Boolean\n            get() = binding.root.isEnabled\n            set(value) {\n                binding.root.isEnabled = value\n            }\n    }\n\n    binding.tips.text = context.getHtml(text)\n\n    impl.configure()\n\n    addElement(impl)\n\n    return impl\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/preference/Value.kt",
    "content": "package yos.clash.material.design.preference\n\ninterface NullableTextAdapter<T> {\n    fun from(value: T): String?\n    fun to(text: String?): T\n\n    companion object {\n        val Port = object : NullableTextAdapter<Int?> {\n            override fun from(value: Int?): String? {\n                if (value == null) return null\n\n                return if (value > 0) value.toString() else \"\"\n            }\n\n            override fun to(text: String?): Int? {\n                if (text == null) return null\n\n                return text.toIntOrNull() ?: 0\n            }\n        }\n\n        val String = object : NullableTextAdapter<String?> {\n            override fun from(value: String?): String? {\n                return value\n            }\n\n            override fun to(text: String?): String? {\n                return text\n            }\n        }\n    }\n}\n\ninterface TextAdapter<T> {\n    fun from(value: T): String\n    fun to(text: String): T\n\n    companion object {\n        val String = object : TextAdapter<String> {\n            override fun from(value: String): String {\n                return value\n            }\n\n            override fun to(text: String): String {\n                return text\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/store/UiStore.kt",
    "content": "package yos.clash.material.design.store\n\nimport android.content.Context\nimport yos.clash.material.common.store.Store\nimport yos.clash.material.common.store.asStoreProvider\nimport com.github.kr328.clash.core.model.ProxySort\nimport yos.clash.material.design.model.AppInfoSort\nimport yos.clash.material.design.model.DarkMode\n\nclass UiStore(context: Context) {\n    private val store = Store(\n        context\n            .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)\n            .asStoreProvider()\n    )\n\n    var enableVpn: Boolean by store.boolean(\n        key = \"enable_vpn\",\n        defaultValue = true\n    )\n\n    var darkMode: DarkMode by store.enum(\n        key = \"dark_mode\",\n        defaultValue = DarkMode.Auto,\n        values = DarkMode.values()\n    )\n\n    var proxyExcludeNotSelectable by store.boolean(\n        key = \"proxy_exclude_not_selectable\",\n        defaultValue = false,\n    )\n\n    var proxySingleLine: Boolean by store.boolean(\n        key = \"proxy_single_line\",\n        defaultValue = false\n    )\n\n    var proxySort: ProxySort by store.enum(\n        key = \"proxy_sort\",\n        defaultValue = ProxySort.Default,\n        values = ProxySort.values()\n    )\n\n    var proxyLastGroup: String by store.string(\n        key = \"proxy_last_group\",\n        defaultValue = \"\"\n    )\n\n    var accessControlSort: AppInfoSort by store.enum(\n        key = \"access_control_sort\",\n        defaultValue = AppInfoSort.Label,\n        values = AppInfoSort.values(),\n    )\n\n    var accessControlReverse: Boolean by store.boolean(\n        key = \"access_control_reverse\",\n        defaultValue = false\n    )\n\n    var accessControlSystemApp: Boolean by store.boolean(\n        key = \"access_control_system_app\",\n        defaultValue = false,\n    )\n\n    companion object {\n        private const val PREFERENCE_NAME = \"ui\"\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ui/DayNight.kt",
    "content": "package yos.clash.material.design.ui\n\nenum class DayNight {\n    Day, Night\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ui/Insets.kt",
    "content": "package yos.clash.material.design.ui\n\ndata class Insets(val start: Int, val top: Int, val end: Int, val bottom: Int) {\n    companion object {\n        val EMPTY = Insets(0, 0, 0, 0)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ui/ObservableCurrentTime.kt",
    "content": "package yos.clash.material.design.ui\n\nimport androidx.databinding.BaseObservable\nimport androidx.databinding.Bindable\nimport androidx.databinding.library.baseAdapters.BR\n\nclass ObservableCurrentTime : BaseObservable() {\n    var value: Long = System.currentTimeMillis()\n        @Bindable get\n        private set(value) {\n            field = value\n\n            notifyPropertyChanged(BR.value)\n        }\n\n    fun update() {\n        value = System.currentTimeMillis()\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ui/Surface.kt",
    "content": "package yos.clash.material.design.ui\n\nimport androidx.databinding.BaseObservable\nimport androidx.databinding.Bindable\nimport yos.clash.material.design.BR\n\nclass Surface : BaseObservable() {\n    var insets: Insets = Insets.EMPTY\n        @Bindable get\n        set(value) {\n            field = value\n\n            notifyPropertyChanged(BR.insets)\n        }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/ui/ToastDuration.kt",
    "content": "package yos.clash.material.design.ui\n\nenum class ToastDuration {\n    Short, Long, Indefinite\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/ActivityBar.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.app.Activity\nimport android.content.Context\nimport android.widget.ImageView\nimport android.widget.TextView\nimport yos.clash.material.design.R\nimport yos.clash.material.design.view.ActivityBarLayout\n\nfun ActivityBarLayout.applyFrom(context: Context) {\n    if (context is Activity) {\n        findViewById<ImageView>(R.id.activity_bar_close_view)?.apply {\n            setOnClickListener {\n                context.onBackPressed()\n            }\n        }\n        findViewById<TextView>(R.id.activity_bar_title_view)?.apply {\n            text = context.title\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/App.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.pm.PackageInfo\nimport android.content.pm.PackageManager\nimport yos.clash.material.common.compat.foreground\nimport yos.clash.material.design.model.AppInfo\n\nfun PackageInfo.toAppInfo(pm: PackageManager): AppInfo {\n    return AppInfo(\n        packageName = packageName,\n        icon = applicationInfo.loadIcon(pm).foreground(),\n        label = applicationInfo.loadLabel(pm).toString(),\n        installTime = firstInstallTime,\n        updateDate = lastUpdateTime,\n    )\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Binding.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.view.View\nimport androidx.databinding.BindingAdapter\n\n@BindingAdapter(\"android:minHeight\")\nfun bindMinHeight(view: View, value: Float) {\n    view.minimumHeight = value.toInt()\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Context.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.app.Activity\nimport android.content.Context\nimport android.text.Spanned\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.annotation.DimenRes\nimport androidx.annotation.StringRes\nimport yos.clash.material.common.compat.fromHtmlCompat\n\nval Context.layoutInflater: LayoutInflater\n    get() = LayoutInflater.from(this)\n\nval Context.root: ViewGroup?\n    get() {\n        return when (this) {\n            is Activity -> {\n                findViewById(android.R.id.content)\n            }\n            else -> {\n                null\n            }\n        }\n    }\n\nfun Context.getPixels(@DimenRes resId: Int): Int {\n    return resources.getDimensionPixelSize(resId)\n}\n\nfun Context.getHtml(@StringRes resId: Int): Spanned {\n    return fromHtmlCompat(getString(resId))\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Diff.kt",
    "content": "package yos.clash.material.design.util\n\nimport androidx.recyclerview.widget.DiffUtil\n\nfun <T> List<T>.diffWith(\n    newList: List<T>,\n    detectMove: Boolean = false,\n    id: (T) -> Any? = { it }\n): DiffUtil.DiffResult {\n    val oldList = this\n\n    return DiffUtil.calculateDiff(object : DiffUtil.Callback() {\n        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {\n            return id(oldList[oldItemPosition]) == id(newList[newItemPosition])\n        }\n\n        override fun getOldListSize(): Int {\n            return oldList.size\n        }\n\n        override fun getNewListSize(): Int {\n            return newList.size\n        }\n\n        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {\n            return oldList[oldItemPosition] == newList[newItemPosition]\n        }\n    }, detectMove)\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Elevation.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.animation.ValueAnimator\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.R\nimport yos.clash.material.design.view.ActivityBarLayout\nimport yos.clash.material.design.view.ObservableScrollView\n\nprivate class AppBarElevationController(\n    private val activityBar: ActivityBarLayout\n) {\n    private var animator: ValueAnimator? = null\n\n    var elevated: Boolean = false\n        set(value) {\n            if (field == value)\n                return\n\n            field = value\n\n            animator?.end()\n\n            animator = if (value) {\n                ValueAnimator.ofFloat(\n                    activityBar.elevation,\n                    activityBar.context.getPixels(R.dimen.toolbar_elevation).toFloat()\n                )\n            } else {\n                ValueAnimator.ofFloat(\n                    activityBar.elevation,\n                    0f\n                )\n            }.apply {\n                addUpdateListener {\n                    activityBar.elevation = it.animatedValue as Float\n                }\n\n                start()\n            }\n        }\n}\n\nfun RecyclerView.bindAppBarElevation(activityBar: ActivityBarLayout) {\n    addOnScrollListener(object : RecyclerView.OnScrollListener() {\n        private val controller = AppBarElevationController(activityBar)\n\n        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {\n            controller.elevated = !recyclerView.isTop\n        }\n    })\n}\n\nfun ObservableScrollView.bindAppBarElevation(activityBar: ActivityBarLayout) {\n    val controller = AppBarElevationController(activityBar)\n\n    addOnScrollChangedListener { view, _, _, _, _ ->\n        controller.elevated = !view.isTop\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/I18n.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.Context\nimport com.github.kr328.clash.core.model.Provider\nimport yos.clash.material.common.compat.preferredLocale\nimport yos.clash.material.design.R\nimport yos.clash.material.service.model.Profile\nimport java.text.SimpleDateFormat\nimport java.util.*\n\nprivate const val DATE_DATE_ONLY = \"yyyy-MM-dd\"\nprivate const val DATE_TIME_ONLY = \"HH:mm:ss.SSS\"\nprivate const val DATE_ALL = \"$DATE_DATE_ONLY $DATE_TIME_ONLY\"\n\nfun Profile.Type.toString(context: Context): String {\n    return when (this) {\n        Profile.Type.File -> context.getString(R.string.file)\n        Profile.Type.Url -> context.getString(R.string.url)\n        Profile.Type.External -> context.getString(R.string.external)\n    }\n}\n\nfun Provider.type(context: Context): String {\n    val type = when (type) {\n        Provider.Type.Proxy -> context.getString(R.string.proxy)\n        Provider.Type.Rule -> context.getString(R.string.rule)\n    }\n\n    val vehicle = when (vehicleType) {\n        Provider.VehicleType.HTTP -> context.getString(R.string.http)\n        Provider.VehicleType.File -> context.getString(R.string.file)\n        Provider.VehicleType.Compatible -> context.getString(R.string.compatible)\n    }\n\n    return context.getString(R.string.format_provider_type, type, vehicle)\n}\n\n@JvmOverloads\nfun Date.format(\n    context: Context,\n    includeDate: Boolean = true,\n    includeTime: Boolean = true,\n): String {\n    val locale = context.resources.configuration.preferredLocale\n\n    return when {\n        includeDate && includeTime ->\n            SimpleDateFormat(DATE_ALL, locale).format(this)\n\n        includeDate ->\n            SimpleDateFormat(DATE_DATE_ONLY, locale).format(this)\n\n        includeTime ->\n            SimpleDateFormat(DATE_TIME_ONLY, locale).format(this)\n\n        else -> \"\"\n    }\n}\n\nfun Long.formatDate(\n    context: Context,\n    includeDate: Boolean = true,\n    includeTime: Boolean = true,\n): String = Date(this).format(context, includeDate, includeTime)\n\nfun Long.toBytesString(): String {\n    return when {\n        this > 1024 * 1024 * 1024 * 1024L ->\n            String.format(\"%.2f TiB\", (this.toDouble() / 1024 / 1024 / 1024 / 1024))\n\n        this > 1024 * 1024 * 1024 ->\n            String.format(\"%.2f GiB\", (this.toDouble() / 1024 / 1024 / 1024))\n\n        this > 1024 * 1024 ->\n            String.format(\"%.2f MiB\", (this.toDouble() / 1024 / 1024))\n\n        this > 1024 ->\n            String.format(\"%.2f KiB\", (this.toDouble() / 1024))\n\n        else ->\n            \"$this Bytes\"\n    }\n}\n\nfun progress(used: Long, total: Long): Int {\n    return ((used.toFloat() / total.toFloat()) * 100).toInt()\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Inserts.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.view.View\nimport androidx.core.view.ViewCompat\nimport androidx.core.view.WindowInsetsCompat\nimport yos.clash.material.design.ui.Insets\n\nfun View.setOnInsertsChangedListener(adaptLandscape: Boolean = true, listener: (Insets) -> Unit) {\n    setOnApplyWindowInsetsListener { v, ins ->\n        val compat = WindowInsetsCompat.toWindowInsetsCompat(ins)\n        val insets = compat.getInsets(WindowInsetsCompat.Type.systemBars())\n\n        val rInsets = if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {\n            Insets(\n                insets.left,\n                insets.top,\n                insets.right,\n                insets.bottom,\n            )\n        } else {\n            Insets(\n                insets.right,\n                insets.top,\n                insets.left,\n                insets.bottom,\n            )\n        }\n\n        listener(if (adaptLandscape) rInsets.landscape(v.context) else rInsets)\n\n        compat.toWindowInsets()!!\n    }\n\n    requestApplyInsets()\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Interval.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.Context\nimport yos.clash.material.design.R\nimport java.util.concurrent.TimeUnit\n\nfun Long.elapsedIntervalString(context: Context): String {\n    val day = TimeUnit.MILLISECONDS.toDays(this)\n    val hour = TimeUnit.MILLISECONDS.toHours(this)\n    val minute = TimeUnit.MILLISECONDS.toMinutes(this)\n\n    return when {\n        day > 0 -> context.getString(R.string.format_days_ago, day)\n        hour > 0 -> context.getString(R.string.format_hours_ago, hour)\n        minute > 0 -> context.getString(R.string.format_minutes_ago, minute)\n        else -> context.getString(R.string.recently)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Landscape.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.Context\nimport yos.clash.material.design.R\nimport yos.clash.material.design.ui.Insets\n\nfun Insets.landscape(context: Context): Insets {\n    val displayMetrics = context.resources.displayMetrics\n    val minWidth = context.getPixels(R.dimen.surface_landscape_min_width)\n\n    val width = displayMetrics.widthPixels\n    val height = displayMetrics.heightPixels\n\n    return if (width > height && width > minWidth) {\n        val expectedWidth = width.coerceAtMost(height.coerceAtLeast(minWidth))\n\n        val padding = (width - expectedWidth).coerceAtLeast(start + end) / 2\n\n        copy(start = padding.coerceAtLeast(start), end = padding.coerceAtLeast(end))\n    } else {\n        this\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/ListView.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.Context\nimport android.view.View\nimport android.view.View.MeasureSpec\nimport android.widget.FrameLayout\nimport android.widget.ListAdapter\n\nfun ListAdapter.measureWidth(context: Context): Int {\n    val parent = FrameLayout(context)\n\n    val widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)\n    val heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)\n\n    var itemView: View? = null\n    var maxWidth = 0\n    var itemType = 0\n\n    for (i in 0 until count) {\n        val positionType = getItemViewType(i)\n        if (positionType != itemType) {\n            itemType = positionType\n            itemView = null\n        }\n\n        itemView = getView(i, itemView, parent)\n        itemView.measure(widthMeasureSpec, heightMeasureSpec)\n\n        val itemWidth: Int = itemView.measuredWidth\n\n        if (itemWidth > maxWidth) {\n            maxWidth = itemWidth\n        }\n    }\n\n    return maxWidth\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/RecyclerView.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.Context\nimport android.view.View\nimport androidx.core.view.children\nimport androidx.databinding.Observable\nimport androidx.recyclerview.widget.GridLayoutManager\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport yos.clash.material.design.BR\nimport yos.clash.material.design.ui.Surface\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport kotlin.reflect.KMutableProperty0\n\nfun RecyclerView.applyLinearAdapter(context: Context, adapter: RecyclerView.Adapter<*>) {\n    this.layoutManager = LinearLayoutManager(context)\n    this.adapter = adapter\n}\n\nsuspend fun <H : RecyclerView.ViewHolder, T> RecyclerView.Adapter<H>.swapDataSet(\n    property: KMutableProperty0<List<T>>,\n    newDataset: List<T>,\n    compareEquals: Boolean = true\n) {\n    val ignore = withContext(Dispatchers.Default) {\n        compareEquals && property.get() == newDataset\n    }\n\n    if (ignore) return\n\n    withContext(Dispatchers.Main) {\n        if (property.get().size == newDataset.size) {\n            property.set(newDataset)\n\n            notifyItemRangeChanged(0, newDataset.size)\n        } else {\n            notifyItemRangeRemoved(0, property.get().size)\n\n            property.set(newDataset)\n\n            notifyItemRangeInserted(0, newDataset.size)\n        }\n    }\n}\n\nsuspend fun <H : RecyclerView.ViewHolder, T> RecyclerView.Adapter<H>.patchDataSet(\n    property: KMutableProperty0<List<T>>,\n    newDataset: List<T>,\n    detectMove: Boolean = false,\n    id: (T) -> Any? = { it }\n) {\n    val result = withContext(Dispatchers.Default) {\n        property.get().diffWith(newDataset, detectMove, id)\n    }\n\n    withContext(Dispatchers.Main) {\n        property.set(newDataset)\n        result.dispatchUpdatesTo(this@patchDataSet)\n    }\n}\n\nfun RecyclerView.invalidateChildren() {\n    children.forEach {\n        it.postInvalidate()\n    }\n}\n\nfun RecyclerView.bindInsets(surface: Surface, top: Int = 0, bottom: Int = 0) {\n    fun applyInsets() {\n        val t = surface.insets.top + top\n        val b = surface.insets.bottom + bottom\n\n        setPaddingRelative(0, t, 0, b)\n    }\n\n    surface.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {\n        override fun onPropertyChanged(sender: Observable?, propertyId: Int) {\n            if (propertyId == BR.insets) {\n                applyInsets()\n            }\n        }\n    })\n\n    applyInsets()\n}\n\nfun RecyclerView.addScrolledToBottomObserver(listener: (RecyclerView, Boolean) -> Unit) {\n    val observer = object : RecyclerView.OnScrollListener() {\n        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {\n            if (newState == RecyclerView.SCROLL_STATE_IDLE) {\n                listener(this@addScrolledToBottomObserver, recyclerView.isBottom)\n            }\n        }\n    }\n\n    addOnScrollListener(observer)\n}\n\nval RecyclerView.firstVisibleView: View?\n    get() {\n        return when (val mgr = layoutManager) {\n            is LinearLayoutManager ->\n                mgr.findViewByPosition(mgr.findFirstVisibleItemPosition())\n            else ->\n                throw UnsupportedOperationException(\"unsupported manager: $mgr\")\n        }\n    }\n\nval RecyclerView.isTop: Boolean\n    get() = computeHorizontalScrollOffset() == 0 && computeVerticalScrollOffset() == 0\n\nval RecyclerView.isBottom: Boolean\n    get() {\n        return when (val mgr = layoutManager) {\n            is GridLayoutManager -> {\n                mgr.findFirstVisibleItemPosition() != 0 &&\n                        mgr.findLastVisibleItemPosition() == adapter!!.itemCount - 1\n            }\n            else -> {\n                throw UnsupportedOperationException(\"unsupported layout manager\")\n            }\n        }\n    }"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/ScrollView.kt",
    "content": "package yos.clash.material.design.util\n\nimport yos.clash.material.design.view.ObservableScrollView\n\nval ObservableScrollView.isTop: Boolean\n    get() = scrollX == 0 && scrollY == 0\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Theme.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport android.util.TypedValue\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport yos.clash.material.common.compat.getDrawableCompat\nimport yos.clash.material.design.R\n\ninterface ClickableScope {\n    fun focusable(defaultValue: Boolean): Boolean\n    fun clickable(defaultValue: Boolean): Boolean\n    fun background(): Drawable?\n    fun foreground(): Drawable?\n}\n\nval Context.selectableItemBackground: Drawable?\n    get() {\n        return getDrawableCompat(resolveThemedResourceId(android.R.attr.selectableItemBackground))\n    }\n\nfun Context.resolveClickableAttrs(\n    attributeSet: AttributeSet?,\n    @AttrRes defaultAttrRes: Int = 0,\n    @StyleRes defaultStyleRes: Int = 0,\n    block: ClickableScope.() -> Unit,\n) {\n    theme.obtainStyledAttributes(\n        attributeSet,\n        R.styleable.Clickable,\n        defaultAttrRes,\n        defaultStyleRes\n    ).apply {\n        val impl = object : ClickableScope {\n            override fun focusable(defaultValue: Boolean): Boolean {\n                return getBoolean(R.styleable.Clickable_android_focusable, defaultValue)\n            }\n\n            override fun clickable(defaultValue: Boolean): Boolean {\n                return getBoolean(R.styleable.Clickable_android_clickable, defaultValue)\n            }\n\n            override fun background(): Drawable? {\n                return getDrawable(R.styleable.Clickable_android_background)\n            }\n\n            override fun foreground(): Drawable? {\n                return getDrawable(R.styleable.Clickable_android_focusable)\n            }\n\n        }\n\n        impl.apply(block)\n\n        recycle()\n    }\n}\n\nfun Context.resolveThemedColor(@AttrRes resId: Int): Int {\n    return TypedValue().apply {\n        theme.resolveAttribute(resId, this, true)\n    }.data\n}\n\nfun Context.resolveThemedBoolean(@AttrRes resId: Int): Boolean {\n    return TypedValue().apply {\n        theme.resolveAttribute(resId, this, true)\n    }.data != 0\n}\n\nfun Context.resolveThemedResourceId(@AttrRes resId: Int): Int {\n    return TypedValue().apply {\n        theme.resolveAttribute(resId, this, true)\n    }.resourceId\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Toast.kt",
    "content": "package yos.clash.material.design.util\n\nimport yos.clash.material.design.Design\nimport yos.clash.material.design.R\nimport yos.clash.material.design.ui.ToastDuration\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\n\nsuspend fun Design<*>.showExceptionToast(message: CharSequence) {\n    showToast(message, ToastDuration.Long) {\n        setAction(R.string.detail) {\n            MaterialAlertDialogBuilder(it.context)\n                .setTitle(R.string.error)\n                .setMessage(message)\n                .setCancelable(true)\n                .setPositiveButton(R.string.ok) { _, _ -> }\n                .show()\n        }\n    }\n}\n\nsuspend fun Design<*>.showExceptionToast(exception: Exception) {\n    showExceptionToast(exception.message ?: \"Unknown\")\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/Validator.kt",
    "content": "package yos.clash.material.design.util\n\nimport yos.clash.material.common.util.PatternFileName\n\ntypealias Validator = (String) -> Boolean\n\nval ValidatorAcceptAll: Validator = {\n    true\n}\n\nval ValidatorFileName: Validator = {\n    PatternFileName.matches(it) && it.isNotBlank()\n}\n\nval ValidatorNotBlank: Validator = {\n    it.isNotBlank()\n}\n\nval ValidatorHttpUrl: Validator = {\n    it.startsWith(\"https://\", ignoreCase = true) || it.startsWith(\"http://\", ignoreCase = true)\n}\n\nval ValidatorAutoUpdateInterval: Validator = {\n    it.isEmpty() || (it.toLongOrNull() ?: 0) >= 15\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/util/View.kt",
    "content": "package yos.clash.material.design.util\n\nimport android.view.View\nimport android.view.inputmethod.InputMethodManager\nimport androidx.core.content.getSystemService\n\nfun View.requestTextInput() {\n    post {\n        requestFocus()\n\n        postDelayed({\n            context.getSystemService<InputMethodManager>()\n                ?.showSoftInput(this, 0)\n        }, 300)\n    }\n}\n"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/ActionLabel.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.databinding.ComponentActionLabelBinding\nimport yos.clash.material.design.util.layoutInflater\n\nclass ActionLabel @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {\n    private val binding = ComponentActionLabelBinding\n        .inflate(context.layoutInflater, this, true)\n\n    var icon: Drawable?\n        get() = binding.iconView.background\n        set(value) {\n            binding.iconView.background = value\n            binding.iconView.visibility = if (value == null) View.GONE else View.VISIBLE\n        }\n\n    var text: CharSequence?\n        get() = binding.textView.text\n        set(value) {\n            binding.textView.text = value\n        }\n\n    var subtext: CharSequence?\n        get() = binding.subtextView.text\n        set(value) {\n            binding.subtextView.text = value\n            binding.subtextView.visibility = if (value == null) View.GONE else View.VISIBLE\n        }\n\n    override fun setOnClickListener(l: OnClickListener?) {\n        binding.clickAble.setOnClickListener(l)\n    }\n\n    init {\n        context.theme.obtainStyledAttributes(\n            attributeSet,\n            R.styleable.ActionLabel,\n            defStyleAttr,\n            defStyleRes\n        ).apply {\n            try {\n                icon = getDrawable(R.styleable.ActionLabel_icon)\n                text = getString(R.styleable.ActionLabel_text)\n                subtext = getString(R.styleable.ActionLabel_subtext)\n            } finally {\n                recycle()\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/ActionTextField.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.databinding.ComponentActionTextFieldBinding\nimport yos.clash.material.design.util.layoutInflater\n\nclass ActionTextField @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {\n    private val binding = ComponentActionTextFieldBinding\n        .inflate(context.layoutInflater, this, true)\n\n    var icon: Drawable?\n        get() = binding.iconView.background\n        set(value) {\n            binding.iconView.background = value\n        }\n\n    var title: CharSequence?\n        get() = binding.titleView.text\n        set(value) {\n            binding.titleView.text = value\n        }\n\n    var text: CharSequence?\n        get() = binding.textView.text\n        set(value) {\n            if (isEnabled)\n                binding.textView.text = value\n            else\n                binding.textView.text = context.getText(R.string.unavailable)\n        }\n\n    var placeholder: CharSequence?\n        get() = binding.textView.hint\n        set(value) {\n            binding.textView.hint = value\n        }\n\n    override fun setEnabled(enabled: Boolean) {\n        super.setEnabled(enabled)\n\n        if (enabled) {\n            binding.root.alpha = 1.0f\n            binding.actionView.isFocusable = true\n            binding.actionView.isClickable = true\n        } else {\n            binding.root.alpha = 0.33f\n            binding.actionView.isFocusable = false\n            binding.actionView.isClickable = false\n        }\n\n        text = text\n    }\n\n    override fun setOnClickListener(l: OnClickListener?) {\n        binding.actionView.setOnClickListener(l)\n    }\n\n    init {\n        context.theme.obtainStyledAttributes(\n            attributeSet,\n            R.styleable.ActionTextField,\n            defStyleAttr,\n            defStyleRes\n        ).apply {\n            try {\n                isEnabled = getBoolean(R.styleable.ActionTextField_enabled, true)\n                icon = getDrawable(R.styleable.ActionTextField_icon)\n                title = getString(R.styleable.ActionTextField_title)\n                text = getString(R.styleable.ActionTextField_text)\n                placeholder = getString(R.styleable.ActionTextField_placeholder)\n            } finally {\n                recycle()\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/ActivityBarLayout.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.MotionEvent\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport yos.clash.material.design.util.resolveThemedColor\n\nclass ActivityBarLayout @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {\n    init {\n        alpha = 0.96f\n\n        setBackgroundColor(context.resolveThemedColor(android.R.attr.windowBackground))\n    }\n\n    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {\n        super.dispatchTouchEvent(ev)\n\n        return true\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/AppRecyclerView.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.recyclerview.widget.RecyclerView\n\nclass AppRecyclerView @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0\n) : RecyclerView(context, attributeSet, defStyleAttr) {\n    init {\n        isFocusable = false\n    }\n\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/LargeActionCard.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.databinding.ComponentLargeActionLabelBinding\nimport yos.clash.material.design.util.*\nimport com.google.android.material.card.MaterialCardView\n\nclass LargeActionCard @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0\n) : MaterialCardView(context, attributeSet, defStyleAttr) {\n    private val binding = ComponentLargeActionLabelBinding\n        .inflate(context.layoutInflater, this, true)\n\n    var text: CharSequence?\n        get() = binding.textView.text\n        set(value) {\n            binding.textView.text = value\n        }\n\n    var subtext: CharSequence?\n        get() = binding.subtextView.text\n        set(value) {\n            binding.subtextView.text = value\n        }\n\n    var icon: Drawable?\n        get() = binding.iconView.background\n        set(value) {\n            binding.iconView.background = value\n        }\n\n    init {\n        context.resolveClickableAttrs(attributeSet, defStyleAttr) {\n            isFocusable = focusable(true)\n            isClickable = clickable(true)\n            foreground = foreground() ?: context.selectableItemBackground\n        }\n\n        context.theme.obtainStyledAttributes(\n            attributeSet,\n            R.styleable.LargeActionCard,\n            defStyleAttr,\n            0\n        ).apply {\n            try {\n                icon = getDrawable(R.styleable.LargeActionCard_icon)\n                text = getString(R.styleable.LargeActionCard_text)\n                subtext = getString(R.styleable.LargeActionCard_subtext)\n            } finally {\n                recycle()\n            }\n        }\n\n        minimumHeight = context.getPixels(R.dimen.large_action_card_min_height)\n        radius = context.getPixels(R.dimen.large_action_card_radius).toFloat()\n        elevation = context.getPixels(R.dimen.large_action_card_elevation).toFloat()\n        setCardBackgroundColor(context.resolveThemedColor(R.attr.colorSurface))\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/LargeActionLabel.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport yos.clash.material.design.R\nimport yos.clash.material.design.databinding.ComponentLargeActionLabelBinding\nimport yos.clash.material.design.util.layoutInflater\nimport yos.clash.material.design.util.resolveClickableAttrs\nimport yos.clash.material.design.util.selectableItemBackground\n\nclass LargeActionLabel @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {\n    private val binding = ComponentLargeActionLabelBinding\n        .inflate(context.layoutInflater, this, true)\n\n    var icon: Drawable?\n        get() = binding.iconView.background\n        set(value) {\n            binding.iconView.background = value\n        }\n\n    var text: CharSequence?\n        get() = binding.textView.text\n        set(value) {\n            binding.textView.text = value\n        }\n\n    var subtext: CharSequence?\n        get() = binding.subtextView.text\n        set(value) {\n            binding.subtextView.text = value\n\n            if (value == null) {\n                binding.subtextView.visibility = View.GONE\n            } else {\n                binding.subtextView.visibility = View.VISIBLE\n            }\n        }\n\n    init {\n        context.resolveClickableAttrs(\n            attributeSet,\n            defStyleAttr,\n            defStyleRes\n        ) {\n            isFocusable = focusable(true)\n            isClickable = clickable(true)\n            background = background() ?: context.selectableItemBackground\n        }\n\n        context.theme.obtainStyledAttributes(\n            attributeSet,\n            R.styleable.LargeActionLabel,\n            defStyleAttr,\n            defStyleRes\n        ).apply {\n            try {\n                icon = getDrawable(R.styleable.LargeActionLabel_icon)\n                text = getString(R.styleable.LargeActionLabel_text)\n                subtext = getString(R.styleable.LargeActionLabel_subtext)\n            } finally {\n                recycle()\n            }\n        }\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/ObservableScrollView.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.ScrollView\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\n\nclass ObservableScrollView @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : ScrollView(context, attributeSet, defStyleAttr, defStyleRes) {\n    fun interface OnScrollChangedListener {\n        fun onChanged(scrollView: ObservableScrollView, x: Int, y: Int, oldl: Int, oldt: Int)\n    }\n\n    private val scrollChangedListeners: MutableSet<OnScrollChangedListener> = mutableSetOf()\n\n    override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {\n        super.onScrollChanged(l, t, oldl, oldt)\n\n        scrollChangedListeners.forEach {\n            it.onChanged(this, l, t, oldl, oldt)\n        }\n    }\n\n    fun addOnScrollChangedListener(listener: OnScrollChangedListener) {\n        scrollChangedListeners.add(listener)\n    }\n}"
  },
  {
    "path": "design/src/main/java/yos/clash/material/design/view/VerticalScrollableHost.kt",
    "content": "package yos.clash.material.design.view\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.MotionEvent\nimport android.widget.FrameLayout\nimport kotlin.math.absoluteValue\nimport kotlin.math.tan\n\nclass VerticalScrollableHost @JvmOverloads constructor(\n    context: Context,\n    attributeSet: AttributeSet? = null,\n    defStyleAttr: Int = 0,\n    defStyleRes: Int = 0\n) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {\n    private var initialX = 0f\n    private var initialY = 0f\n\n    private val degree = tan(Math.toRadians(15.0))\n\n    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {\n        val parentView = parent ?: return super.onInterceptTouchEvent(ev)\n\n        if (ev.action == MotionEvent.ACTION_DOWN) {\n            initialX = ev.x\n            initialY = ev.y\n            parentView.requestDisallowInterceptTouchEvent(true)\n        } else if (ev.action == MotionEvent.ACTION_MOVE) {\n            val dx = ev.x - initialX\n            val dy = ev.y - initialY\n\n            val t = dy.absoluteValue / dx.absoluteValue\n\n            if (t < degree) {\n                parentView.requestDisallowInterceptTouchEvent(false)\n            }\n        }\n\n        return super.onInterceptTouchEvent(ev)\n    }\n}"
  },
  {
    "path": "design/src/main/res/drawable/bg_bottom_sheet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"?attr/colorSurface\" />\n            <corners android:radius=\"20dp\" />\n        </shape>\n    </item>\n    <item\n        android:gravity=\"top|center_horizontal\"\n        android:top=\"@dimen/bottom_sheet_background_padding_top\">\n        <shape android:shape=\"rectangle\">\n            <size\n                android:width=\"40dp\"\n                android:height=\"@dimen/bottom_sheet_header_height\" />\n            <solid android:color=\"?attr/colorControlHighlight\" />\n            <corners android:radius=\"5dp\" />\n        </shape>\n    </item>\n</layer-list>"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_adb.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4L5,12v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM15,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_add.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_apps.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_arrow_back.xml",
    "content": "<vector android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M11.67,3.87L9.9,2.1 0,12l9.9,9.9 1.77,-1.77L3.54,12z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_assignment.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_attach_file.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_brightness_4.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-0.89,0 -1.74,-0.2 -2.5,-0.55C11.56,16.5 13,14.42 13,12s-1.44,-4.5 -3.5,-5.45C10.26,6.2 11.11,6 12,6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_clear_all.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M5,13h14v-2L5,11v2zM3,17h14v-2L3,15v2zM7,7v2h14L21,7L7,7z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_close.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_cloud_download.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_content_copy.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_delete.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_dns.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20,13H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM7,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM20,3H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1zM7,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_domain.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,7V3H2v18h20V7H12zM6,19H4v-2h2V19zM6,15H4v-2h2V15zM6,11H4V9h2V11zM6,7H4V5h2V7zM10,19H8v-2h2V19zM10,15H8v-2h2V15zM10,11H8V9h2V11zM10,7H8V5h2V7zM20,19h-8v-2h2v-2h-2v-2h2v-2h-2V9h8V19zM18,11h-2v2h2V11zM18,15h-2v2h2V15z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_edit.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_extension.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_flash_on.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M7,2v11h3v9l7,-12h-4l4,-8z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_get_app.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z\"/>\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_help_center.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM12.01,18c-0.7,0 -1.26,-0.56 -1.26,-1.26c0,-0.71 0.56,-1.25 1.26,-1.25c0.71,0 1.25,0.54 1.25,1.25C13.25,17.43 12.72,18 12.01,18zM15.02,10.6c-0.76,1.11 -1.48,1.46 -1.87,2.17c-0.16,0.29 -0.22,0.48 -0.22,1.41h-1.82c0,-0.49 -0.08,-1.29 0.31,-1.98c0.49,-0.87 1.42,-1.39 1.96,-2.16c0.57,-0.81 0.25,-2.33 -1.37,-2.33c-1.06,0 -1.58,0.8 -1.8,1.48L8.56,8.49C9.01,7.15 10.22,6 11.99,6c1.48,0 2.49,0.67 3.01,1.52C15.44,8.24 15.7,9.59 15.02,10.6z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_info.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_more_vert.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_publish.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M5,4v2h14L19,4L5,4zM5,14h4v6h6v-6h4l-7,-7 -7,7z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_replay.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_restore.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_save.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_search.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_settings.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_stop.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M6,6h12v12H6z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_swap_vert.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_swap_vertical_circle.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM6.5,9L10,5.5 13.5,9L11,9v4L9,13L9,9L6.5,9zM17.5,15L14,18.5 10.5,15L13,15v-4h2v4h2.5z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_sync.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_update.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M21,10.12h-6.78l2.74,-2.82c-2.73,-2.7 -7.15,-2.8 -9.88,-0.1c-2.73,2.71 -2.73,7.08 0,9.79s7.15,2.71 9.88,0C18.32,15.65 19,14.08 19,12.1h2c0,1.98 -0.88,4.55 -2.64,6.29c-3.51,3.48 -9.21,3.48 -12.72,0c-3.5,-3.47 -3.53,-9.11 -0.02,-12.58s9.14,-3.47 12.65,0L21,3V10.12zM12.5,8v4.25l3.5,2.08l-0.72,1.21L11,13V8H12.5z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_view_list.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M4,14h4v-4L4,10v4zM4,19h4v-4L4,15v4zM4,9h4L8,5L4,5v4zM9,14h12v-4L9,10v4zM9,19h12v-4L9,15v4zM9,5v4h12L21,5L9,5z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_vpn_lock.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5L17,4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1L23,5c0,-0.55 -0.45,-1 -1,-1zM21.2,4h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7L21.2,4zM18.92,12c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L7,13v-2h2c0.55,0 1,-0.45 1,-1L10,8h2c1.1,0 2,-0.9 2,-2L14,3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_baseline_work.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_clash.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"128\"\n    android:viewportHeight=\"128\">\n    <path\n        android:fillColor=\"@color/color_clash_light\"\n        android:pathData=\"M28.86,97.02c0.71,-6.53 1.36,-12.87 2.11,-19.19c0.89,-7.48 1.79,-14.96 2.85,-22.42c1.12,-7.86 2.36,-15.71 3.69,-23.55c0.55,-3.27 1.4,-6.49 2.22,-9.71c0.39,-1.53 1.05,-1.76 2.33,-0.79c1.11,0.84 2.21,1.82 2.99,2.97c2.2,3.23 4.25,6.56 6.29,9.89c0.45,0.73 0.86,0.95 1.69,0.77c7.29,-1.66 14.6,-1.44 21.89,-0.02c1.12,0.22 1.58,-0.07 2.1,-1.05c1.89,-3.56 3.89,-7.06 5.9,-10.56c0.41,-0.71 1.01,-1.33 1.61,-1.89c1.12,-1.05 1.83,-0.89 2.2,0.58c0.74,2.94 1.53,5.88 2.02,8.87c2.42,14.82 4.86,29.63 7.05,44.49c1.21,8.27 1.96,16.61 2.85,24.93c0.32,3.01 0.1,3.19 -2.95,3.47c-13.86,1.26 -27.75,1.13 -41.64,0.99c-6.9,-0.07 -13.79,-0.45 -20.69,-0.78c-1.51,-0.07 -2.98,-0.64 -4.49,-0.86c-1.35,-0.2 -2.73,-0.19 -4.09,-0.36c-4.29,-0.52 -8.07,-2.08 -10.64,-5.74c-4.36,-6.2 -3.01,-16.57 6.71,-19.52c0.84,-0.25 1.92,-0.13 2.76,0.17c1.05,0.38 1.12,1.32 0.25,2.05c-1.02,0.85 -2.15,1.57 -3.21,2.37c-1.66,1.26 -3.01,2.77 -3.43,4.9c-0.69,3.55 1.07,7.03 4.3,8.61C23.83,96.74 26.3,96.89 28.86,97.02zM49.17,49.98c-2.12,-0.01 -3.8,1.66 -3.8,3.78c-0.01,2.13 1.65,3.8 3.77,3.81c2.12,0.01 3.79,-1.65 3.8,-3.78C52.95,51.67 51.29,49.99 49.17,49.98zM79.88,57.57c2.12,-0.01 3.77,-1.69 3.76,-3.82c-0.01,-2.12 -1.69,-3.78 -3.81,-3.76c-2.11,0.01 -3.77,1.7 -3.76,3.82C76.08,55.93 77.76,57.58 79.88,57.57zM64,62.62c1.94,1.64 3.99,1.71 4.65,0.01c-0.47,0.05 -0.87,0.04 -1.23,0.14c-1.09,0.32 -1.99,0.05 -2.63,-0.86c-0.65,-0.92 -1.13,-0.68 -1.69,0.07c-0.63,0.84 -1.45,1.17 -2.51,0.85c-0.37,-0.11 -0.8,-0.02 -1.33,-0.02C60.33,64.36 62.08,64.23 64,62.62z\"\n        tools:ignore=\"VectorPath\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_article.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,5v14H5V5H19M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3L19,3z\" />\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M14,17H7v-2h7V17zM17,13H7v-2h10V13zM17,9H7V7h10V9z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_check_circle.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM16.59,7.58L10,14.17l-2.59,-2.58L6,13l4,4 8,-8z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_delete.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_folder.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9.17,6l2,2H20v10H4V6h5.17M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_inbox.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19v-3h3.56c0.69,1.19 1.97,2 3.45,2s2.75,-0.81 3.45,-2L19,16v3zM19,14h-4.99c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2L5,14L5,5h14v9z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_info.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_label.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16zM16,17H5V7h11l3.55,5L16,17z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_not_interested.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zM18.31,16.9L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/ic_outline_update.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M11,8v5l4.25,2.52l0.77,-1.28l-3.52,-2.09V8H11zM21,10V3l-2.64,2.64C16.74,4.01 14.49,3 12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9h-2c0,3.86 -3.14,7 -7,7s-7,-3.14 -7,-7s3.14,-7 7,-7c1.93,0 3.68,0.79 4.95,2.05L14,10H21z\" />\n</vector>\n"
  },
  {
    "path": "design/src/main/res/drawable/yos_shape.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape android:shape=\"rectangle\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners android:radius=\"20dp\" />\n    <!--android:topLeftRadius=\"16dp\"\n    android:topRightRadius=\"16dp\"-->\n</shape>"
  },
  {
    "path": "design/src/main/res/drawable/yos_shape_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape android:shape=\"rectangle\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"?attr/colorSurface\" />\n    <corners android:radius=\"20dp\" />\n    <!--android:topLeftRadius=\"16dp\"\n    android:topRightRadius=\"16dp\"-->\n</shape>"
  },
  {
    "path": "design/src/main/res/layout/adapter_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"app\"\n            type=\"yos.clash.material.design.model.AppInfo\" />\n        <variable\n            name=\"selected\"\n            type=\"boolean\" />\n    </data>\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n            android:background=\"@{app.icon}\"\n            android:clickable=\"false\"\n            android:focusable=\"false\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/switch_view\"\n            android:layout_toEndOf=\"@id/icon_view\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@{app.label}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:text=\"@{app.packageName}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n\n        <com.google.android.material.checkbox.MaterialCheckBox\n            android:id=\"@+id/switch_view\"\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:checked=\"@{selected}\"\n            android:clickable=\"false\"\n            android:focusable=\"false\"\n            android:gravity=\"center_horizontal|end\" />\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_editable_text_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\"\n        android:paddingStart=\"@dimen/item_header_margin\"\n        android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n        <TextView\n            android:id=\"@+id/text_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentStart=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/delete_view\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n        <ImageView\n            android:id=\"@+id/delete_view\"\n            android:layout_width=\"@dimen/item_tailing_component_size\"\n            android:layout_height=\"@dimen/item_tailing_component_size\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"?attr/selectableItemBackgroundBorderless\"\n            android:clickable=\"true\"\n            android:contentDescription=\"@string/delete\"\n            android:focusable=\"true\"\n            android:padding=\"@dimen/toolbar_image_action_padding\"\n            android:src=\"@drawable/ic_baseline_close\" />\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_editable_text_map.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\"\n        android:paddingStart=\"@dimen/item_header_margin\"\n        android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n        <TextView\n            android:id=\"@+id/key_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentStart=\"true\"\n            android:layout_toStartOf=\"@id/delete_view\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n        <TextView\n            android:id=\"@+id/value_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/key_view\"\n            android:layout_alignParentStart=\"true\"\n            android:layout_toStartOf=\"@id/delete_view\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n\n        <ImageView\n            android:id=\"@+id/delete_view\"\n            android:layout_width=\"@dimen/item_tailing_component_size\"\n            android:layout_height=\"@dimen/item_tailing_component_size\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"?attr/selectableItemBackgroundBorderless\"\n            android:clickable=\"true\"\n            android:contentDescription=\"@string/delete\"\n            android:focusable=\"true\"\n            android:padding=\"@dimen/toolbar_image_action_padding\"\n            android:src=\"@drawable/ic_baseline_close\" />\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_file.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"file\"\n            type=\"yos.clash.material.design.model.File\" />\n        <variable\n            name=\"currentTime\"\n            type=\"yos.clash.material.design.ui.ObservableCurrentTime\" />\n        <variable\n            name=\"open\"\n            type=\"android.view.View.OnClickListener\" />\n        <variable\n            name=\"more\"\n            type=\"android.view.View.OnClickListener\" />\n\n        <import type=\"yos.clash.material.design.util.I18nKt\" />\n        <import type=\"yos.clash.material.design.util.IntervalKt\" />\n        <import type=\"android.view.View\" />\n    </data>\n\n    <RelativeLayout\n        android:id=\"@+id/root_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:nextFocusRight=\"@id/menu_view\"\n        android:onClick=\"@{open}\">\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/item_tailing_component_size\"\n            android:layout_height=\"@dimen/item_tailing_component_size\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n            android:background=\"@{file.directory ? @drawable/ic_outline_folder : @drawable/ic_outline_article}\"\n            android:backgroundTint=\"?attr/colorControlNormal\"\n            android:gravity=\"center\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/elapsed_view\"\n            android:layout_toEndOf=\"@id/icon_view\"\n            android:orientation=\"vertical\"\n            android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@{file.name}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:text=\"@{file.directory ? null : I18nKt.toBytesString(file.size)}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n                android:visibility=\"@{file.directory ? View.GONE : View.VISIBLE}\" />\n        </LinearLayout>\n\n        <TextView\n            android:id=\"@+id/elapsed_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginEnd=\"@dimen/item_midden_margin\"\n            android:layout_toStartOf=\"@id/menu_view\"\n            android:text=\"@{file.directory ? null : IntervalKt.elapsedIntervalString(currentTime.value - file.lastModified, context)}\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Tooltip\"\n            android:visibility=\"@{file.directory ? View.GONE : View.VISIBLE}\" />\n\n        <View\n            android:layout_width=\"@dimen/divider_size\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/menu_view\"\n            android:background=\"?attr/colorControlHighlight\"\n            android:minHeight=\"@{@dimen/item_tailing_component_size * 1.5f}\" />\n\n        <FrameLayout\n            android:id=\"@+id/menu_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"?attr/selectableItemBackground\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:nextFocusLeft=\"@id/root_view\"\n            android:onClick=\"@{more}\">\n\n            <View\n                android:layout_width=\"@dimen/item_tailing_component_size\"\n                android:layout_height=\"@dimen/item_tailing_component_size\"\n                android:layout_margin=\"@dimen/item_tailing_margin\"\n                android:background=\"@drawable/ic_baseline_more_vert\" />\n        </FrameLayout>\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_log_message.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"message\"\n            type=\"com.github.kr328.clash.core.model.LogMessage\" />\n        <import type=\"yos.clash.material.design.util.I18nKt\" />\n    </data>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"vertical\"\n        android:paddingHorizontal=\"@dimen/logcat_padding_horizontal\"\n        android:paddingVertical=\"@dimen/logcat_padding_vertical\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"start|center_vertical\"\n                android:text=\"@{message.level.name()}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n                android:textColor=\"?colorControlActivated\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"end|center_vertical\"\n                android:text=\"@{I18nKt.format(message.time, context, false, true)}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n                android:textColor=\"?colorControlActivated\" />\n        </LinearLayout>\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/item_text_margin\"\n            android:text=\"@{message.message}\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"profile\"\n            type=\"yos.clash.material.service.model.Profile\" />\n        <variable\n            name=\"currentTime\"\n            type=\"yos.clash.material.design.ui.ObservableCurrentTime\" />\n        <variable\n            name=\"clicked\"\n            type=\"android.view.View.OnClickListener\" />\n        <variable\n            name=\"menu\"\n            type=\"android.view.View.OnClickListener\" />\n\n        <import type=\"yos.clash.material.design.util.I18nKt\" />\n        <import type=\"yos.clash.material.design.util.IntervalKt\" />\n        <import type=\"android.view.View\" />\n    </data>\n\n    <RelativeLayout\n        android:id=\"@+id/root_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:nextFocusRight=\"@id/menu_view\"\n        android:onClick=\"@{clicked}\">\n\n        <RadioButton\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"26dp\"\n            android:layout_height=\"26dp\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n            android:checked=\"@{profile.active}\"\n            android:clickable=\"false\"\n            android:focusable=\"false\"\n            android:gravity=\"center\" />\n        <!--否则显示不全-->\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/menu_view\"\n            android:layout_marginEnd=\"@dimen/item_midden_margin\"\n            android:layout_toEndOf=\"@id/icon_view\"\n            android:orientation=\"vertical\"\n            android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"start|bottom\"\n                android:orientation=\"horizontal\">\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@{profile.name}\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginStart=\"@dimen/item_text_margin\"\n                    android:text=\"@{profile.pending ? @string/format_type_unsaved(I18nKt.toString(profile.type, context)) : @string/format_type_saved(I18nKt.toString(profile.type, context), IntervalKt.elapsedIntervalString(currentTime.value - profile.updatedAt, context))}\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle2\"\n                    android:alpha=\"0.5\" />\n            </LinearLayout>\n\n            <com.google.android.material.progressindicator.LinearProgressIndicator\n                android:id=\"@+id/progress_indicator\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:max=\"100\"\n                android:progress=\"@{profile.moreInfo.totalBase == 0 ? 0 : I18nKt.progress(profile.moreInfo.uploadBase + profile.moreInfo.downloadBase,profile.moreInfo.totalBase)}\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:orientation=\"horizontal\"\n                android:alpha=\"0.5\"\n                android:visibility=\"@{profile.moreInfo == null ? View.GONE : View.VISIBLE}\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@{@string/format_profile_usage(I18nKt.toBytesString(profile.moreInfo.uploadBase + profile.moreInfo.downloadBase), I18nKt.toBytesString(profile.moreInfo.totalBase))}\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginStart=\"@dimen/item_text_margin\"\n                    android:text=\"@{I18nKt.formatDate(profile.moreInfo.expireTime, context, true, false)}\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n                    android:visibility=\"@{profile.moreInfo.expireTime &lt;= 0 ? View.GONE : View.VISIBLE}\"\n                    android:ellipsize=\"start\"\n                    android:singleLine=\"true\" />\n            </LinearLayout>\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:visibility=\"@{profile.moreInfo == null ? View.VISIBLE : View.GONE}\"\n                android:text=\"--\"\n                android:alpha=\"0.5\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n        <!--<View\n            android:layout_width=\"@dimen/divider_size\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/menu_view\"\n            android:background=\"?attr/colorControlHighlight\"\n            android:minHeight=\"@{@dimen/item_tailing_component_size * 1.5f}\" />-->\n        <!--分割-->\n\n        <FrameLayout\n            android:id=\"@+id/menu_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"?attr/selectableItemBackground\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:nextFocusLeft=\"@id/root_view\"\n            android:onClick=\"@{menu}\">\n\n            <View\n                android:layout_width=\"@dimen/item_tailing_component_size\"\n                android:layout_height=\"@dimen/item_tailing_component_size\"\n                android:layout_margin=\"@dimen/item_tailing_margin\"\n                android:background=\"@drawable/ic_baseline_more_vert\" />\n        </FrameLayout>\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_profile_provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"provider\"\n            type=\"yos.clash.material.design.model.ProfileProvider\" />\n    </data>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n        <View\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n            android:background=\"@{provider.icon}\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@{provider.name}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:text=\"@{provider.summary}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"provider\"\n            type=\"com.github.kr328.clash.core.model.Provider\" />\n        <variable\n            name=\"state\"\n            type=\"yos.clash.material.design.model.ProviderState\" />\n        <variable\n            name=\"currentTime\"\n            type=\"yos.clash.material.design.ui.ObservableCurrentTime\" />\n        <variable\n            name=\"update\"\n            type=\"android.view.View.OnClickListener\" />\n\n        <import type=\"yos.clash.material.design.util.I18nKt\" />\n        <import type=\"yos.clash.material.design.util.IntervalKt\" />\n        <import type=\"android.view.View\" />\n    </data>\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:paddingStart=\"@dimen/item_header_margin\"\n        android:paddingEnd=\"0dp\">\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentStart=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/elapsed_view\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@{provider.name}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:text=\"@{I18nKt.type(provider, context)}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n\n        <TextView\n            android:id=\"@+id/elapsed_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginEnd=\"@dimen/item_midden_margin\"\n            android:layout_toStartOf=\"@id/end_view\"\n            android:text=\"@{IntervalKt.elapsedIntervalString(currentTime.value - state.updatedAt, context)}\" />\n\n        <View\n            android:layout_width=\"@dimen/divider_size\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/end_view\"\n            android:background=\"?attr/colorControlHighlight\"\n            android:minHeight=\"@{@dimen/item_tailing_component_size * 1.5f}\" />\n\n        <FrameLayout\n            android:id=\"@+id/end_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"?attr/selectableItemBackground\"\n            android:clickable=\"@{!state.updating}\"\n            android:focusable=\"@{!state.updating}\"\n            android:nextFocusLeft=\"@id/root_view\"\n            android:onClick=\"@{update}\">\n\n            <ImageView\n                android:id=\"@+id/update_view\"\n                android:layout_width=\"@dimen/item_tailing_component_size\"\n                android:layout_height=\"@dimen/item_tailing_component_size\"\n                android:layout_gravity=\"center\"\n                android:layout_margin=\"@dimen/item_tailing_margin\"\n                android:contentDescription=\"@string/more\"\n                android:src=\"@drawable/ic_baseline_swap_vert\"\n                android:visibility=\"@{state.updating ? View.GONE : View.VISIBLE}\" />\n\n            <ProgressBar\n                android:layout_width=\"@dimen/item_tailing_component_size\"\n                android:layout_height=\"@dimen/item_tailing_component_size\"\n                android:layout_gravity=\"center\"\n                android:layout_margin=\"@dimen/item_tailing_margin\"\n                android:visibility=\"@{state.updating ? View.VISIBLE : View.GONE}\" />\n        </FrameLayout>\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/adapter_sideload_provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"appInfo\"\n            type=\"yos.clash.material.design.model.AppInfo\" />\n        <variable\n            name=\"selected\"\n            type=\"boolean\" />\n\n        <import type=\"android.view.View\" />\n    </data>\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_alignParentStart=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n            android:background=\"@{appInfo.icon}\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/selected_view\"\n            android:layout_toEndOf=\"@id/icon_view\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/label_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@{appInfo.label}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:id=\"@+id/package_name_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:text=\"@{appInfo.packageName}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n                android:visibility=\"@{appInfo.packageName.length() == 0 ? View.GONE : View.VISIBLE}\" />\n        </LinearLayout>\n\n        <com.google.android.material.radiobutton.MaterialRadioButton\n            android:id=\"@+id/selected_view\"\n            android:layout_width=\"26dp\"\n            android:layout_height=\"26dp\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginHorizontal=\"@dimen/item_tailing_margin\"\n            android:checked=\"@{selected}\"\n            android:clickable=\"false\"\n            android:focusable=\"false\"\n            android:gravity=\"center\" />\n        <!--否则显示不全-->\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/common_activity_bar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:minHeight=\"@dimen/toolbar_height\"\n    android:orientation=\"vertical\">\n\n    <ImageView\n        android:id=\"@+id/activity_bar_close_view\"\n        android:layout_width=\"@dimen/item_back_component_size\"\n        android:layout_height=\"@dimen/item_back_component_size\"\n        android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n        android:layout_marginTop=\"20dp\"\n        android:background=\"?attr/selectableItemBackgroundBorderless\"\n        android:clickable=\"true\"\n        android:contentDescription=\"@string/close\"\n        android:focusable=\"true\"\n        android:src=\"@drawable/ic_baseline_arrow_back\" />\n\n    <TextView\n        android:id=\"@+id/activity_bar_title_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:paddingHorizontal=\"@dimen/toolbar_padding_horizontal\"\n        android:paddingTop=\"50dp\"\n        android:paddingBottom=\"15dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline4\" />\n</LinearLayout>"
  },
  {
    "path": "design/src/main/res/layout/common_recycler_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"insets\"\n            type=\"yos.clash.material.design.ui.Insets\" />\n    </data>\n\n    <yos.clash.material.design.view.AppRecyclerView\n        android:id=\"@+id/recycler_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        android:paddingTop=\"@{(float) insets.top + @dimen/toolbar_height}\"\n        android:paddingBottom=\"@{insets.bottom}\" />\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/component_action_label.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.card.MaterialCardView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:cardBackgroundColor=\"?attr/colorSurface\"\n            app:cardCornerRadius=\"8dp\"\n            app:cardElevation=\"0dp\"\n            app:strokeWidth=\"0dp\">\n            <LinearLayout\n                android:background=\"?attr/colorSurface\"\n                android:clickable=\"true\"\n                android:focusable=\"true\"\n                android:id=\"@+id/click_able\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                android:paddingVertical=\"@dimen/item_padding_vertical\"\n                android:paddingHorizontal=\"@dimen/item_header_margin\">\n                <View\n                    android:id=\"@+id/icon_view\"\n                    android:layout_width=\"@dimen/item_header_component_size\"\n                    android:layout_height=\"@dimen/item_header_component_size\"\n                    android:layout_marginEnd=\"@dimen/item_header_margin\" />\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:orientation=\"vertical\">\n\n                    <TextView\n                        android:id=\"@+id/text_view\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n                    <TextView\n                        android:id=\"@+id/subtext_view\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"@dimen/item_text_margin\"\n                        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n                </LinearLayout>\n            </LinearLayout>\n        </com.google.android.material.card.MaterialCardView>\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/component_action_text_field.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:orientation=\"horizontal\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_marginHorizontal=\"@dimen/item_header_margin\" />\n\n        <FrameLayout\n            android:id=\"@+id/action_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"@dimen/item_tailing_margin\"\n            android:background=\"?attr/selectableItemBackground\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_vertical\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\" />\n\n                <TextView\n                    android:id=\"@+id/text_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"@dimen/item_text_margin\"\n                    android:layout_marginEnd=\"@dimen/item_tailing_component_size\"\n                    android:ellipsize=\"start\"\n                    android:singleLine=\"true\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n                <View\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/divider_size\"\n                    android:background=\"?attr/colorControlNormal\" />\n            </LinearLayout>\n\n            <ImageView\n                android:layout_width=\"@dimen/item_tailing_component_size\"\n                android:layout_height=\"@dimen/item_tailing_component_size\"\n                android:layout_gravity=\"bottom|end\"\n                android:layout_marginBottom=\"@dimen/divider_size\"\n                android:contentDescription=\"@string/edit\"\n                android:padding=\"@dimen/toolbar_image_action_padding\"\n                android:src=\"@drawable/ic_baseline_edit\" />\n        </FrameLayout>\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/component_large_action_label.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout>\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:paddingVertical=\"@dimen/large_item_padding_vertical\">\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/large_item_header_component_size\"\n            android:layout_height=\"@dimen/large_item_header_component_size\"\n            android:layout_marginHorizontal=\"@dimen/large_item_header_margin_horizontal\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"@dimen/large_item_tailing_margin_horizontal\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/text_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:id=\"@+id/subtext_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/large_item_text_margin\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"versionName\"\n            type=\"String\" />\n    </data>\n    <com.google.android.material.card.MaterialCardView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:cardBackgroundColor=\"?attr/colorSurface\"\n        app:cardCornerRadius=\"12dp\"\n        app:cardElevation=\"0dp\"\n        app:strokeWidth=\"0dp\">\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\"\n            android:padding=\"@dimen/dialog_padding\">\n\n            <View\n                android:layout_width=\"@dimen/about_icon_size\"\n                android:layout_height=\"@dimen/about_icon_size\"\n            android:background=\"@drawable/ic_clash\"\n            android:backgroundTint=\"?attr/colorLogo\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"@dimen/about_text_margin\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/application_name\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:text=\"@{versionName}\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n        </LinearLayout>\n    </com.google.android.material.card.MaterialCardView>\n\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_access_control.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.AccessControlDesign\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <include\n            android:id=\"@+id/main_list\"\n            layout=\"@layout/common_recycler_list\"\n            app:insets=\"@{self.surface.insets}\" />\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_toStartOf=\"@id/search_view\" />\n\n                <ImageView\n                    android:id=\"@+id/search_view\"\n                    android:layout_width=\"@dimen/item_tailing_component_size\"\n                    android:layout_height=\"@dimen/item_tailing_component_size\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_marginTop=\"18dp\"\n                    android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                    android:layout_toStartOf=\"@id/menu_view\"\n                    android:background=\"?attr/selectableItemBackgroundBorderless\"\n                    android:clickable=\"true\"\n                    android:contentDescription=\"@string/search\"\n                    android:focusable=\"true\"\n                    android:padding=\"@dimen/toolbar_image_action_padding\"\n                    android:src=\"@drawable/ic_baseline_search\" />\n\n                <ImageView\n                    android:id=\"@+id/menu_view\"\n                    android:layout_width=\"@dimen/item_tailing_component_size\"\n                    android:layout_height=\"@dimen/item_tailing_component_size\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_marginTop=\"18dp\"\n                    android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                    android:background=\"?attr/selectableItemBackgroundBorderless\"\n                    android:clickable=\"true\"\n                    android:contentDescription=\"@string/more\"\n                    android:focusable=\"true\"\n                    android:padding=\"@dimen/toolbar_image_action_padding\"\n                    android:src=\"@drawable/ic_baseline_more_vert\" />\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_app_crashed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.AppCrashedDesign\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <yos.clash.material.design.view.ObservableScrollView\n            android:id=\"@+id/scroll_root\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:paddingTop=\"@{(float) self.surface.insets.top + @dimen/toolbar_height}\"\n            android:paddingBottom=\"@{self.surface.insets.bottom}\">\n\n            <TextView\n                android:id=\"@+id/logs_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:lineSpacingMultiplier=\"1.5\"\n                android:paddingHorizontal=\"@dimen/logcat_padding_horizontal\"\n                android:paddingVertical=\"@dimen/logcat_padding_vertical\"\n                android:textIsSelectable=\"true\" />\n        </yos.clash.material.design.view.ObservableScrollView>\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\"\n            android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n            <include layout=\"@layout/common_activity_bar\" />\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_files.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.FilesDesign\" />\n        <variable\n            name=\"currentInBaseDir\"\n            type=\"boolean\" />\n        <variable\n            name=\"configurationEditable\"\n            type=\"boolean\" />\n\n        <import type=\"android.view.View\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <include\n            android:id=\"@+id/main_list\"\n            layout=\"@layout/common_recycler_list\"\n            app:insets=\"@{self.surface.insets}\" />\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_toStartOf=\"@id/new_view\" />\n\n                <ImageView\n                    android:id=\"@+id/new_view\"\n                    android:layout_width=\"@dimen/item_tailing_component_size\"\n                    android:layout_height=\"@dimen/item_tailing_component_size\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_marginTop=\"18dp\"\n                    android:layout_gravity=\"end|center_vertical\"\n                    android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                    android:background=\"?attr/selectableItemBackgroundBorderless\"\n                    android:clickable=\"true\"\n                    android:contentDescription=\"@string/_new\"\n                    android:focusable=\"true\"\n                    android:onClick=\"@{() -> self.requestNew()}\"\n                    android:padding=\"@dimen/toolbar_image_action_padding\"\n                    android:src=\"@drawable/ic_baseline_add\"\n                    android:visibility=\"@{currentInBaseDir ? View.GONE : View.VISIBLE}\" />\n\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_logcat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.LogcatDesign\" />\n        <variable\n            name=\"streaming\"\n            type=\"boolean\" />\n        <import type=\"android.view.View\" />\n        <import type=\"yos.clash.material.design.LogcatDesign.Request\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{(float) self.surface.insets.top + @dimen/toolbar_height}\"\n            android:paddingBottom=\"@{self.surface.insets.bottom}\">\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/yos_shape_color\">\n                <yos.clash.material.design.view.AppRecyclerView\n                    android:id=\"@+id/recycler_list\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    android:clipToPadding=\"false\" />\n            </LinearLayout>\n        </LinearLayout>\n\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_toStartOf=\"@id/action_layout\" />\n\n                <FrameLayout\n                    android:id=\"@+id/action_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_marginTop=\"18dp\">\n\n                    <LinearLayout\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:visibility=\"@{streaming ? View.GONE : View.VISIBLE}\">\n\n                        <ImageView\n                            android:layout_width=\"@dimen/item_tailing_component_size\"\n                            android:layout_height=\"@dimen/item_tailing_component_size\"\n                            android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                            android:background=\"?selectableItemBackgroundBorderless\"\n                            android:clickable=\"true\"\n                            android:contentDescription=\"@string/delete\"\n                            android:focusable=\"true\"\n                            android:onClick=\"@{() -> self.requests.offer(Request.Delete)}\"\n                            android:padding=\"@dimen/toolbar_image_action_padding\"\n                            android:src=\"@drawable/ic_baseline_delete\" />\n\n                        <ImageView\n                            android:layout_width=\"@dimen/item_tailing_component_size\"\n                            android:layout_height=\"@dimen/item_tailing_component_size\"\n                            android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                            android:background=\"?selectableItemBackgroundBorderless\"\n                            android:clickable=\"true\"\n                            android:contentDescription=\"@string/export\"\n                            android:focusable=\"true\"\n                            android:onClick=\"@{() -> self.requests.offer(Request.Export)}\"\n                            android:padding=\"@dimen/toolbar_image_action_padding\"\n                            android:src=\"@drawable/ic_baseline_publish\" />\n                    </LinearLayout>\n\n                    <ImageView\n                        android:layout_width=\"@dimen/item_tailing_component_size\"\n                        android:layout_height=\"@dimen/item_tailing_component_size\"\n                        android:layout_gravity=\"end\"\n                        android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                        android:background=\"?selectableItemBackgroundBorderless\"\n                        android:clickable=\"true\"\n                        android:contentDescription=\"@string/close\"\n                        android:focusable=\"true\"\n                        android:onClick=\"@{() -> self.requests.offer(Request.Close)}\"\n                        android:padding=\"@dimen/toolbar_image_action_padding\"\n                        android:src=\"@drawable/ic_baseline_stop\"\n                        android:visibility=\"@{streaming ? View.VISIBLE : View.GONE}\" />\n                </FrameLayout>\n\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_logs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.LogsDesign\" />\n        <import type=\"yos.clash.material.design.LogsDesign.Request\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <yos.clash.material.design.view.ActivityBarLayout\n                android:id=\"@+id/activity_bar_layout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"@{self.surface.insets.top}\">\n\n                <RelativeLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                    <include\n                        layout=\"@layout/common_activity_bar\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_alignParentStart=\"true\"\n                        android:layout_centerVertical=\"true\"\n                        android:layout_toStartOf=\"@id/delete_view\" />\n\n                    <ImageView\n                        android:id=\"@+id/delete_view\"\n                        android:layout_width=\"@dimen/item_tailing_component_size\"\n                        android:layout_height=\"@dimen/item_tailing_component_size\"\n                        android:layout_alignParentEnd=\"true\"\n                        android:layout_centerVertical=\"false\"\n                        android:layout_marginTop=\"18dp\"\n                        android:layout_gravity=\"end|center_vertical\"\n                        android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                        android:background=\"?attr/selectableItemBackgroundBorderless\"\n                        android:clickable=\"true\"\n                        android:contentDescription=\"@string/delete_all_logs\"\n                        android:focusable=\"true\"\n                        android:onClick=\"@{() -> self.requests.offer(Request.DeleteAll.INSTANCE)}\"\n                        android:padding=\"@dimen/toolbar_image_action_padding\"\n                        android:src=\"@drawable/ic_baseline_clear_all\" />\n                </RelativeLayout>\n            </yos.clash.material.design.view.ActivityBarLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingBottom=\"@{self.surface.insets.bottom}\">\n                <com.google.android.material.card.MaterialCardView\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    app:cardBackgroundColor=\"@android:color/transparent\"\n                    app:cardCornerRadius=\"20dp\"\n                    app:cardElevation=\"0dp\"\n                    app:strokeWidth=\"0dp\">\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\">\n                        <yos.clash.material.design.view.ActionLabel\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:onClick=\"@{() -> self.requests.offer(Request.StartLogcat.INSTANCE)}\"\n                            app:icon=\"@drawable/ic_baseline_adb\"\n                            app:subtext=\"@string/tap_to_start\"\n                            app:text=\"@string/clash_logcat\" />\n\n                        <View\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"1dp\"\n                            android:background=\"?attr/colorControlHighlight\" />\n\n                        <TextView\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:paddingVertical=\"@dimen/item_padding_vertical\"\n                            android:paddingHorizontal=\"@dimen/item_tailing_margin\"\n                            android:text=\"@string/history\"\n                            android:textColor=\"?attr/colorControlActivated\" />\n                        <!--android:paddingStart=\"@{@dimen/item_header_component_size + @dimen/item_header_margin * 2}\"-->\n\n\n                        <androidx.recyclerview.widget.RecyclerView\n                            android:id=\"@+id/recycler_list\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"match_parent\"\n                            android:clipToPadding=\"false\" />\n                    </LinearLayout>\n                </com.google.android.material.card.MaterialCardView>\n            </LinearLayout>\n        </LinearLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.MainDesign\" />\n        <variable\n            name=\"clashRunning\"\n            type=\"boolean\" />\n        <variable\n            name=\"forwarded\"\n            type=\"String\" />\n        <variable\n            name=\"mode\"\n            type=\"String\" />\n        <variable\n            name=\"profileName\"\n            type=\"String\" />\n        <variable\n            name=\"colorClashStarted\"\n            type=\"int\" />\n        <variable\n            name=\"colorClashStopped\"\n            type=\"int\" />\n        <variable\n            name=\"hasProviders\"\n            type=\"boolean\" />\n\n        <import type=\"android.view.View\" />\n        <import type=\"yos.clash.material.design.MainDesign.Request\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <ScrollView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:scrollbars=\"none\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:animateLayoutChanges=\"true\"\n                android:clipToPadding=\"false\"\n                android:orientation=\"vertical\"\n                android:paddingHorizontal=\"@dimen/main_padding_horizontal\"\n                android:paddingTop=\"110dp\"\n                android:paddingBottom=\"@{self.surface.insets.bottom}\">\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center_vertical\"\n                    android:minHeight=\"@dimen/main_top_banner_height\"\n                    android:orientation=\"horizontal\">\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/launch_name\"\n                        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline4\" />\n                    <FrameLayout\n                        android:layout_width=\"@dimen/large_item_header_layout_size\"\n                        android:layout_height=\"@dimen/large_item_header_layout_size\">\n\n                        <View\n                            android:layout_width=\"@dimen/main_logo_size\"\n                            android:layout_height=\"@dimen/main_logo_size\"\n                            android:layout_gravity=\"center\"\n                            android:background=\"@drawable/ic_clash\"\n                            android:backgroundTint=\"?attr/colorLogo\" />\n                    </FrameLayout>\n\n                </LinearLayout>\n\n                <yos.clash.material.design.view.LargeActionCard\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_card_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.ToggleStatus)}\"\n                    android:theme=\"@style/AppThemeDark\"\n                    app:cardBackgroundColor=\"@{clashRunning ? colorClashStarted : colorClashStopped}\"\n                    app:icon=\"@{clashRunning ? @drawable/ic_outline_check_circle : @drawable/ic_outline_not_interested}\"\n                    app:subtext=\"@{clashRunning ? @string/format_traffic_forwarded(forwarded) : @string/tap_to_start}\"\n                    app:text=\"@{clashRunning ? @string/running : @string/stopped}\" />\n\n                <yos.clash.material.design.view.LargeActionCard\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_card_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenProxy)}\"\n                    android:visibility=\"@{clashRunning ? View.VISIBLE : View.GONE}\"\n                    app:icon=\"@drawable/ic_baseline_apps\"\n                    app:subtext=\"@{mode}\"\n                    app:text=\"@string/proxy\" />\n\n                <yos.clash.material.design.view.LargeActionCard\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_card_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenProfiles)}\"\n                    app:icon=\"@drawable/ic_baseline_view_list\"\n                    app:subtext=\"@{profileName != null ? @string/format_profile_activated(profileName) : @string/not_selected}\"\n                    app:text=\"@string/profile\" />\n\n                <yos.clash.material.design.view.LargeActionLabel\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_marginVertical=\"@dimen/main_label_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenProviders)}\"\n                    android:visibility=\"@{clashRunning &amp;&amp; hasProviders ? View.VISIBLE : View.GONE}\"\n                    app:icon=\"@drawable/ic_baseline_swap_vertical_circle\"\n                    app:text=\"@string/providers\" />\n\n                <yos.clash.material.design.view.LargeActionLabel\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_label_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenLogs)}\"\n                    app:icon=\"@drawable/ic_baseline_assignment\"\n                    app:text=\"@string/logs\" />\n\n                <yos.clash.material.design.view.LargeActionLabel\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_label_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenSettings)}\"\n                    app:icon=\"@drawable/ic_baseline_settings\"\n                    app:text=\"@string/settings\" />\n\n                <yos.clash.material.design.view.LargeActionLabel\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_label_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenHelp)}\"\n                    app:icon=\"@drawable/ic_baseline_help_center\"\n                    app:text=\"@string/help\" />\n\n                <yos.clash.material.design.view.LargeActionLabel\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/main_label_margin_vertical\"\n                    android:onClick=\"@{() -> self.request(Request.OpenAbout)}\"\n                    app:icon=\"@drawable/ic_baseline_info\"\n                    app:text=\"@string/about\" />\n            </LinearLayout>\n        </ScrollView>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_new_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.NewProfileDesign\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <include\n            android:id=\"@+id/main_list\"\n            layout=\"@layout/common_recycler_list\"\n            app:insets=\"@{self.surface.insets}\" />\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\"\n            android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n            <include layout=\"@layout/common_activity_bar\" />\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_profiles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.ProfilesDesign\" />\n\n        <import type=\"android.view.View\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <include\n            android:id=\"@+id/main_list\"\n            layout=\"@layout/common_recycler_list\"\n            app:insets=\"@{self.surface.insets}\" />\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_toStartOf=\"@id/update_layout\" />\n\n                <FrameLayout\n                    android:id=\"@+id/update_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_toStartOf=\"@id/add_view\"\n                    android:layout_marginTop=\"18dp\">\n\n                    <ImageView\n                        android:id=\"@+id/update_view\"\n                        android:layout_width=\"@dimen/item_tailing_component_size\"\n                        android:layout_height=\"@dimen/item_tailing_component_size\"\n                        android:layout_gravity=\"end|center_vertical\"\n                        android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                        android:background=\"?attr/selectableItemBackgroundBorderless\"\n                        android:clickable=\"true\"\n                        android:contentDescription=\"@string/_new\"\n                        android:focusable=\"true\"\n                        android:onClick=\"@{() -> self.requestUpdateAll()}\"\n                        android:padding=\"@dimen/toolbar_image_action_padding\"\n                        android:src=\"@drawable/ic_baseline_sync\"\n                        android:visibility=\"gone\" />\n                </FrameLayout>\n\n                <ImageView\n                    android:id=\"@+id/add_view\"\n                    android:layout_width=\"@dimen/item_tailing_component_size\"\n                    android:layout_height=\"@dimen/item_tailing_component_size\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_gravity=\"end|top\"\n                    android:layout_marginTop=\"18dp\"\n                    android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                    android:background=\"?attr/selectableItemBackgroundBorderless\"\n                    android:clickable=\"true\"\n                    android:contentDescription=\"@string/_new\"\n                    android:focusable=\"true\"\n                    android:onClick=\"@{() -> self.requestCreate()}\"\n                    android:padding=\"@dimen/toolbar_image_action_padding\"\n                    android:src=\"@drawable/ic_baseline_add\" />\n\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_properties.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.PropertiesDesign\" />\n        <variable\n            name=\"profile\"\n            type=\"yos.clash.material.service.model.Profile\" />\n        <variable\n            name=\"processing\"\n            type=\"boolean\" />\n\n        <import type=\"yos.clash.material.service.model.Profile.Type\" />\n        <import type=\"android.view.View\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <yos.clash.material.design.view.ObservableScrollView\n            android:id=\"@+id/scroll_root\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:scrollbars=\"none\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\"\n                android:paddingTop=\"@{(float) self.surface.insets.top + @dimen/toolbar_height}\"\n                android:paddingBottom=\"@{self.surface.insets.bottom}\">\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center_vertical\"\n                    android:orientation=\"horizontal\"\n                    android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n                    <View\n                        android:layout_width=\"@dimen/tips_icon_size\"\n                        android:layout_height=\"@dimen/tips_icon_size\"\n                        android:layout_marginHorizontal=\"@dimen/tips_icon_margin\"\n                        android:background=\"@drawable/ic_outline_info\" />\n\n                    <TextView\n                        android:id=\"@+id/tips\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginEnd=\"@dimen/item_tailing_margin\"\n                        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n                </LinearLayout>\n\n                <yos.clash.material.design.view.ActionTextField\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/properties_element_margin_vertical\"\n                    android:onClick=\"@{() -> self.inputName()}\"\n                    app:icon=\"@drawable/ic_outline_label\"\n                    app:placeholder=\"@string/profile_name\"\n                    app:text=\"@{profile.name}\"\n                    app:title=\"@string/name\" />\n\n                <yos.clash.material.design.view.ActionTextField\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/properties_element_margin_vertical\"\n                    android:onClick=\"@{() -> self.inputUrl()}\"\n                    app:enabled=\"@{profile.type != Type.File}\"\n                    app:icon=\"@drawable/ic_outline_inbox\"\n                    app:placeholder=\"@string/accept_http_content\"\n                    app:text=\"@{profile.source}\"\n                    app:title=\"@string/url\" />\n\n                <yos.clash.material.design.view.ActionTextField\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/properties_element_margin_vertical\"\n                    android:onClick=\"@{() -> self.inputInterval()}\"\n                    app:enabled=\"@{profile.type != Type.File}\"\n                    app:icon=\"@drawable/ic_outline_update\"\n                    app:placeholder=\"@string/at_least_15_minutes\"\n                    app:text=\"@{profile.interval == 0 ? @string/disabled : @string/format_minutes(profile.interval / 1000 / 60)}\"\n                    app:title=\"@string/auto_update\" />\n\n                <yos.clash.material.design.view.ActionLabel\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginVertical=\"@dimen/properties_element_margin_vertical\"\n                    android:onClick=\"@{() -> self.requestBrowseFiles()}\"\n                    app:icon=\"@drawable/ic_outline_folder\"\n                    app:subtext=\"@string/browse_configuration_providers\"\n                    app:text=\"@string/browse_files\" />\n            </LinearLayout>\n        </yos.clash.material.design.view.ObservableScrollView>\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_toStartOf=\"@id/action_layout\" />\n\n                <FrameLayout\n                    android:id=\"@+id/action_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_marginTop=\"18dp\"\n                    android:layout_marginStart=\"@dimen/item_tailing_margin\">\n\n                    <ProgressBar\n                        android:layout_width=\"@dimen/item_tailing_component_size\"\n                        android:layout_height=\"@dimen/item_tailing_component_size\"\n                        android:indeterminate=\"true\"\n                        android:visibility=\"@{processing ? View.VISIBLE : View.GONE}\" />\n\n                    <ImageView\n                        android:layout_width=\"@dimen/item_tailing_component_size\"\n                        android:layout_height=\"@dimen/item_tailing_component_size\"\n                        android:layout_gravity=\"end|center_vertical\"\n                        android:background=\"?attr/selectableItemBackgroundBorderless\"\n                        android:clickable=\"true\"\n                        android:contentDescription=\"@string/save\"\n                        android:focusable=\"true\"\n                        android:onClick=\"@{() -> self.requestCommit()}\"\n                        android:padding=\"@dimen/toolbar_image_action_padding\"\n                        android:src=\"@drawable/ic_baseline_save\"\n                        android:visibility=\"@{processing ? View.GONE : View.VISIBLE}\" />\n                </FrameLayout>\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_providers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.ProvidersDesign\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <include\n            android:id=\"@+id/main_list\"\n            layout=\"@layout/common_recycler_list\"\n            app:insets=\"@{self.surface.insets}\" />\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_toStartOf=\"@id/update_view\" />\n\n                <ImageView\n                    android:id=\"@+id/update_view\"\n                    android:layout_width=\"@dimen/item_tailing_component_size\"\n                    android:layout_height=\"@dimen/item_tailing_component_size\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_gravity=\"end|center_vertical\"\n                    android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                    android:background=\"?attr/selectableItemBackgroundBorderless\"\n                    android:clickable=\"true\"\n                    android:contentDescription=\"@string/update_all\"\n                    android:focusable=\"true\"\n                    android:onClick=\"@{() -> self.requestUpdateAll()}\"\n                    android:padding=\"@dimen/toolbar_image_action_padding\"\n                    android:src=\"@drawable/ic_baseline_sync\"\n                    android:layout_marginTop=\"18dp\" />\n\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_proxy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.ProxyDesign\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n\n        <TextView\n            android:id=\"@+id/empty_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"@string/proxy_empty_tips\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline6\"\n            android:visibility=\"gone\" />\n\n        <androidx.viewpager2.widget.ViewPager2\n            android:id=\"@+id/pages_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"horizontal\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <yos.clash.material.design.view.ActivityBarLayout\n                android:id=\"@+id/activity_bar_layout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"@{self.surface.insets.top}\">\n\n                <RelativeLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                    <include\n                        layout=\"@layout/common_activity_bar\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_alignParentStart=\"true\"\n                        android:layout_centerVertical=\"true\"\n                        android:layout_toStartOf=\"@id/url_test_layout\" />\n\n                    <FrameLayout\n                        android:id=\"@+id/url_test_layout\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_centerVertical=\"false\"\n                        android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                        android:layout_toStartOf=\"@id/menu_view\"\n                        android:layout_marginTop=\"18dp\">\n\n                        <ImageView\n                            android:id=\"@+id/url_test_view\"\n                            android:layout_width=\"@dimen/item_tailing_component_size\"\n                            android:layout_height=\"@dimen/item_tailing_component_size\"\n                            android:layout_gravity=\"center\"\n                            android:background=\"?attr/selectableItemBackgroundBorderless\"\n                            android:clickable=\"true\"\n                            android:contentDescription=\"@string/delay_test\"\n                            android:focusable=\"true\"\n                            android:onClick=\"@{() -> self.requestUrlTesting()}\"\n                            android:padding=\"@dimen/toolbar_image_action_padding\"\n                            android:src=\"@drawable/ic_baseline_flash_on\" />\n\n                        <ProgressBar\n                            android:id=\"@+id/url_test_progress_view\"\n                            android:layout_width=\"@dimen/item_tailing_component_size\"\n                            android:layout_height=\"@dimen/item_tailing_component_size\"\n                            android:visibility=\"gone\" />\n                    </FrameLayout>\n\n                    <ImageView\n                        android:id=\"@+id/menu_view\"\n                        android:layout_width=\"@dimen/item_tailing_component_size\"\n                        android:layout_height=\"@dimen/item_tailing_component_size\"\n                        android:layout_alignParentEnd=\"true\"\n                        android:layout_centerVertical=\"false\"\n                        android:layout_marginStart=\"@dimen/item_tailing_margin\"\n                        android:background=\"?attr/selectableItemBackgroundBorderless\"\n                        android:clickable=\"true\"\n                        android:contentDescription=\"@string/more\"\n                        android:focusable=\"true\"\n                        android:padding=\"@dimen/toolbar_image_action_padding\"\n                        android:src=\"@drawable/ic_baseline_more_vert\"\n                        android:layout_marginTop=\"18dp\" />\n\n                </RelativeLayout>\n            </yos.clash.material.design.view.ActivityBarLayout>\n\n            <com.google.android.material.tabs.TabLayout\n                android:id=\"@+id/tab_layout_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/tab_layout_height\"\n                android:alpha=\"0.96\"\n                android:background=\"?android:windowBackground\"\n                app:tabBackground=\"@android:color/transparent\"\n                app:tabMode=\"scrollable\"\n                android:paddingHorizontal=\"14dp\"\n                android:clipToPadding=\"false\" />\n\n            <View\n                android:id=\"@+id/elevation_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/divider_size\"\n                android:alpha=\"0.96\"\n                android:background=\"?android:windowBackground\"\n                android:elevation=\"@dimen/tab_layout_elevation\"\n                android:visibility=\"gone\" />\n        </LinearLayout>\n\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end|bottom\"\n            android:paddingBottom=\"@{self.surface.insets.bottom}\">\n\n            <com.google.android.material.floatingactionbutton.FloatingActionButton\n                android:id=\"@+id/url_test_float_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_margin=\"@dimen/item_tailing_margin\"\n                android:contentDescription=\"@string/delay_test\"\n                android:onClick=\"@{() -> self.requestUrlTesting()}\"\n                android:src=\"@drawable/ic_baseline_flash_on\"\n                app:backgroundTint=\"?attr/colorPrimary\" />\n        </FrameLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.SettingsDesign\" />\n\n        <import type=\"yos.clash.material.design.SettingsDesign.Request\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <yos.clash.material.design.view.ObservableScrollView\n            android:id=\"@+id/scroll_root\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:scrollbars=\"none\">\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"@{(float) self.surface.insets.top + @dimen/toolbar_height}\"\n                android:paddingBottom=\"@{self.surface.insets.bottom}\">\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\">\n                    <com.google.android.material.card.MaterialCardView\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        app:cardBackgroundColor=\"@android:color/transparent\"\n                        app:cardCornerRadius=\"20dp\"\n                        app:cardElevation=\"0dp\"\n                        app:strokeWidth=\"0dp\">\n                        <LinearLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:orientation=\"vertical\">\n                            <!--android:paddingTop=\"@{(float) self.surface.insets.top}\"\n                            android:paddingBottom=\"@{self.surface.insets.bottom}\"-->\n\n                            <yos.clash.material.design.view.ActionLabel\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:onClick=\"@{() -> self.request(Request.StartApp)}\"\n                                app:icon=\"@drawable/ic_baseline_settings\"\n                                app:text=\"@string/app\"\n                                android:layout_marginBottom=\"5dp\" />\n\n                            <yos.clash.material.design.view.ActionLabel\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:onClick=\"@{() -> self.request(Request.StartNetwork)}\"\n                                app:icon=\"@drawable/ic_baseline_dns\"\n                                app:text=\"@string/network\"\n                                android:layout_marginBottom=\"5dp\" />\n\n                            <yos.clash.material.design.view.ActionLabel\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:onClick=\"@{() -> self.request(Request.StartOverride)}\"\n                                app:icon=\"@drawable/ic_baseline_extension\"\n                                app:text=\"@string/override\" />\n\n                        </LinearLayout>\n                    </com.google.android.material.card.MaterialCardView>\n                </LinearLayout>\n            </LinearLayout>\n        </yos.clash.material.design.view.ObservableScrollView>\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\"\n            android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n            <include layout=\"@layout/common_activity_bar\" />\n        </yos.clash.material.design.view.ActivityBarLayout>\n\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_settings_common.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"surface\"\n            type=\"yos.clash.material.design.ui.Surface\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{surface.insets.start}\"\n        android:paddingEnd=\"@{surface.insets.end}\">\n\n        <yos.clash.material.design.view.ObservableScrollView\n            android:id=\"@+id/scroll_root\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:scrollbars=\"none\">\n\n            <FrameLayout\n                android:id=\"@+id/content\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"@{(float) surface.insets.top + @dimen/toolbar_height}\"\n                android:paddingBottom=\"@{surface.insets.bottom}\" />\n        </yos.clash.material.design.view.ObservableScrollView>\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{surface.insets.top}\"\n            android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n            <include layout=\"@layout/common_activity_bar\" />\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/design_settings_overide.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"self\"\n            type=\"yos.clash.material.design.OverrideSettingsDesign\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{self.surface.insets.start}\"\n        android:paddingEnd=\"@{self.surface.insets.end}\">\n\n        <yos.clash.material.design.view.ObservableScrollView\n            android:id=\"@+id/scroll_root\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:scrollbars=\"none\">\n\n            <FrameLayout\n                android:id=\"@+id/content\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"@{(float) self.surface.insets.top + @dimen/toolbar_height}\"\n                android:paddingBottom=\"@{self.surface.insets.bottom}\" />\n        </yos.clash.material.design.view.ObservableScrollView>\n\n        <yos.clash.material.design.view.ActivityBarLayout\n            android:id=\"@+id/activity_bar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@{self.surface.insets.top}\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n                <include\n                    layout=\"@layout/common_activity_bar\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentStart=\"true\"\n                    android:layout_toStartOf=\"@id/clear_view\" />\n\n                <ImageView\n                    android:id=\"@+id/clear_view\"\n                    android:layout_width=\"@dimen/item_tailing_component_size\"\n                    android:layout_height=\"@dimen/item_tailing_component_size\"\n                    android:layout_alignParentEnd=\"true\"\n                    android:layout_centerVertical=\"false\"\n                    android:layout_gravity=\"top|end\"\n                    android:layout_marginTop=\"18dp\"\n                    android:background=\"?attr/selectableItemBackgroundBorderless\"\n                    android:clickable=\"true\"\n                    android:contentDescription=\"@string/reset\"\n                    android:focusable=\"true\"\n                    android:onClick=\"@{() -> self.requestClear()}\"\n                    android:layout_marginHorizontal=\"@dimen/toolbar_image_action_padding\"\n                    android:src=\"@drawable/ic_baseline_replay\" />\n            </RelativeLayout>\n        </yos.clash.material.design.view.ActivityBarLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_editable_map_text_field.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingHorizontal=\"@dimen/dialog_padding\"\n        android:paddingTop=\"@dimen/dialog_padding\"\n        android:paddingBottom=\"@dimen/dialog_button_margin\">\n\n        <com.google.android.material.textfield.TextInputLayout\n            style=\"@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:hint=\"@string/key\">\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/key_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" />\n        </com.google.android.material.textfield.TextInputLayout>\n\n        <com.google.android.material.textfield.TextInputLayout\n            style=\"@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/item_text_margin\"\n            android:hint=\"@string/value\">\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/value_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" />\n        </com.google.android.material.textfield.TextInputLayout>\n\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_fetch_status.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center_vertical\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:orientation=\"vertical\"\n        android:padding=\"@dimen/dialog_padding\">\n\n        <com.google.android.material.progressindicator.LinearProgressIndicator\n            android:id=\"@+id/progress_indicator\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n\n        <TextView\n            android:id=\"@+id/text\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/item_text_margin\" />\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_files_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"master\"\n            type=\"yos.clash.material.design.FilesDesign\" />\n        <variable\n            name=\"self\"\n            type=\"android.app.Dialog\" />\n        <variable\n            name=\"file\"\n            type=\"yos.clash.material.design.model.File\" />\n        <variable\n            name=\"currentInBase\"\n            type=\"boolean\" />\n        <variable\n            name=\"configurationEditable\"\n            type=\"boolean\" />\n\n        <import type=\"android.view.View\" />\n    </data>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:paddingTop=\"30dp\"\n        android:paddingBottom=\"10dp\">\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:onClick=\"@{() -> master.requestImport(self, file)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:visibility=\"@{!file.isDirectory &amp;&amp; (!currentInBase || configurationEditable) ? View.VISIBLE : View.GONE}\"\n            app:icon=\"@drawable/ic_baseline_get_app\"\n            app:text=\"@string/import_\" />\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:onClick=\"@{() -> master.requestExport(self, file)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:visibility=\"@{!file.isDirectory &amp;&amp; file.size > 0 ? View.VISIBLE : View.GONE}\"\n            app:icon=\"@drawable/ic_baseline_publish\"\n            app:text=\"@string/export\" />\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:onClick=\"@{() -> master.requestRename(self, file)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:visibility=\"@{!currentInBase ? View.VISIBLE : View.GONE}\"\n            app:icon=\"@drawable/ic_baseline_edit\"\n            app:text=\"@string/rename\" />\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:onClick=\"@{() -> master.requestDelete(self, file)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:theme=\"@style/ThemeOverlay.ControlColorError\"\n            android:visibility=\"@{!currentInBase ? View.VISIBLE : View.GONE}\"\n            app:icon=\"@drawable/ic_outline_delete\"\n            app:text=\"@string/delete\" />\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_preference_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"surface\"\n            type=\"yos.clash.material.design.ui.Surface\" />\n    </data>\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingStart=\"@{surface.insets.start}\"\n        android:paddingTop=\"@{surface.insets.top}\"\n        android:paddingEnd=\"@{surface.insets.end}\"\n        android:paddingBottom=\"@{surface.insets.bottom}\">\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:paddingBottom=\"@dimen/preference_fullscreen_button_padding\">\n\n            <TextView\n                android:id=\"@+id/title_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignBottom=\"@id/new_view\"\n                android:layout_alignParentStart=\"true\"\n                android:layout_marginHorizontal=\"@dimen/item_header_margin\"\n                android:layout_toStartOf=\"@id/new_view\"\n                android:gravity=\"center_vertical\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline6\" />\n\n            <ImageView\n                android:id=\"@+id/new_view\"\n                android:layout_width=\"@dimen/item_tailing_component_size\"\n                android:layout_height=\"@dimen/item_tailing_component_size\"\n                android:layout_alignParentEnd=\"true\"\n                android:layout_margin=\"@dimen/item_tailing_margin\"\n                android:background=\"?attr/selectableItemBackgroundBorderless\"\n                android:clickable=\"true\"\n                android:contentDescription=\"@string/_new\"\n                android:focusable=\"true\"\n                android:padding=\"@dimen/toolbar_image_action_padding\"\n                android:src=\"@drawable/ic_baseline_add\" />\n\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/main_list\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:layout_above=\"@id/ok_view\"\n                android:layout_below=\"@id/title_view\" />\n\n            <Button\n                android:id=\"@+id/reset_view\"\n                style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentStart=\"true\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_marginStart=\"@dimen/dialog_button_margin\"\n                android:text=\"@string/reset\" />\n\n            <View\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_toStartOf=\"@id/cancel_view\"\n                android:layout_toEndOf=\"@id/reset_view\" />\n\n            <Button\n                android:id=\"@+id/cancel_view\"\n                style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_toStartOf=\"@id/ok_view\"\n                android:text=\"@string/cancel\" />\n\n            <Button\n                android:id=\"@+id/ok_view\"\n                style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentEnd=\"true\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_marginEnd=\"@dimen/dialog_button_margin\"\n                android:text=\"@string/ok\" />\n        </RelativeLayout>\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_profiles_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <data>\n        <variable\n            name=\"master\"\n            type=\"yos.clash.material.design.ProfilesDesign\" />\n        <variable\n            name=\"self\"\n            type=\"android.app.Dialog\" />\n        <variable\n            name=\"profile\"\n            type=\"yos.clash.material.service.model.Profile\" />\n\n        <import type=\"yos.clash.material.service.model.Profile.Type\" />\n        <import type=\"android.view.View\" />\n    </data>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingTop=\"30dp\"\n        android:paddingBottom=\"10dp\">\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"@{() -> master.requestUpdate(self, profile)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:visibility=\"@{profile.imported &amp;&amp; profile.type != Type.File ? View.VISIBLE : View.GONE}\"\n            app:icon=\"@drawable/ic_baseline_update\"\n            app:text=\"@string/update\" />\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"@{() -> master.requestEdit(self, profile)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            app:icon=\"@drawable/ic_baseline_edit\"\n            app:text=\"@string/edit\" />\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"@{() -> master.requestDuplicate(self, profile)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:visibility=\"@{profile.imported ? View.VISIBLE : View.GONE}\"\n            app:icon=\"@drawable/ic_baseline_content_copy\"\n            app:text=\"@string/duplicate\" />\n\n        <yos.clash.material.design.view.LargeActionLabel\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"@{() -> master.requestDelete(self, profile)}\"\n            android:padding=\"@dimen/bottom_sheet_menu_items_padding\"\n            android:theme=\"@style/ThemeOverlay.ControlColorError\"\n            app:icon=\"@drawable/ic_outline_delete\"\n            app:text=\"@string/delete\" />\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <data>\n        <variable\n            name=\"surface\"\n            type=\"yos.clash.material.design.ui.Surface\" />\n    </data>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:paddingStart=\"@{surface.insets.start}\"\n        android:paddingTop=\"@{surface.insets.top}\"\n        android:paddingEnd=\"@{surface.insets.end}\"\n        android:paddingBottom=\"@{surface.insets.bottom}\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\"\n            android:paddingEnd=\"@dimen/item_tailing_margin\">\n\n            <ImageView\n                android:id=\"@+id/close_view\"\n                android:layout_width=\"@dimen/item_header_component_size\"\n                android:layout_height=\"@dimen/item_header_component_size\"\n                android:layout_margin=\"@dimen/item_header_margin\"\n                android:background=\"?attr/selectableItemBackgroundBorderless\"\n                android:clickable=\"true\"\n                android:contentDescription=\"@string/close\"\n                android:focusable=\"true\"\n                android:padding=\"@dimen/toolbar_image_action_padding\"\n                android:src=\"@drawable/ic_baseline_close\" />\n\n            <androidx.appcompat.widget.AppCompatEditText\n                android:id=\"@+id/keyword_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/keyword\"\n                android:singleLine=\"true\" />\n        </LinearLayout>\n\n        <yos.clash.material.design.view.AppRecyclerView\n            android:id=\"@+id/main_list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:paddingBottom=\"@dimen/dialog_padding\" />\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/dialog_text_field.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingHorizontal=\"@dimen/dialog_padding\"\n        android:paddingTop=\"@dimen/dialog_padding\"\n        android:paddingBottom=\"@dimen/dialog_button_margin\">\n\n        <com.google.android.material.textfield.TextInputLayout\n            android:id=\"@+id/text_layout\"\n            style=\"@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:errorEnabled=\"true\">\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/text_field\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" />\n        </com.google.android.material.textfield.TextInputLayout>\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/preference_category.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <TextView\n        android:id=\"@+id/text_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingVertical=\"@dimen/item_sub_padding_vertical\"\n        android:paddingStart=\"@dimen/item_header_margin\"\n        android:paddingEnd=\"@dimen/item_tailing_margin\"\n        android:textColor=\"?attr/colorControlActivated\" />\n    <!--android:paddingStart=\"@{@dimen/item_header_component_size + @dimen/item_header_margin * 2}\"-->\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/preference_clickable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:minHeight=\"@dimen/item_min_height\"\n        android:orientation=\"horizontal\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\"\n        android:paddingHorizontal=\"@dimen/item_tailing_margin\">\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_marginEnd=\"@dimen/item_header_margin\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/title_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:id=\"@+id/summary_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/preference_switch.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\"\n        android:paddingHorizontal=\"@dimen/item_tailing_margin\">\n        <!--android:minHeight=\"@dimen/item_min_height\"-->\n\n        <View\n            android:id=\"@+id/icon_view\"\n            android:layout_width=\"@dimen/item_header_component_size\"\n            android:layout_height=\"@dimen/item_header_component_size\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginEnd=\"@dimen/item_header_margin\"\n            android:clickable=\"false\"\n            android:focusable=\"false\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/switch_view\"\n            android:layout_toEndOf=\"@id/icon_view\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/title_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n            <TextView\n                android:id=\"@+id/summary_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/item_text_margin\"\n                android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n        </LinearLayout>\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/switch_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:clickable=\"false\"\n            android:focusable=\"false\" />\n    </RelativeLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/layout/preference_tips.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:paddingVertical=\"@dimen/item_padding_vertical\">\n\n        <View\n            android:layout_width=\"@dimen/tips_icon_size\"\n            android:layout_height=\"@dimen/tips_icon_size\"\n            android:layout_marginHorizontal=\"@dimen/tips_icon_margin\"\n            android:background=\"@drawable/ic_outline_info\" />\n\n        <TextView\n            android:id=\"@+id/tips\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"@dimen/item_tailing_margin\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\" />\n    </LinearLayout>\n</layout>"
  },
  {
    "path": "design/src/main/res/menu/menu_access_control.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/select_all\"\n        android:title=\"@string/select_all\" />\n    <item\n        android:id=\"@+id/select_none\"\n        android:title=\"@string/select_none\" />\n    <item\n        android:id=\"@+id/select_invert\"\n        android:title=\"@string/select_invert\" />\n\n    <item android:title=\"@string/filter\">\n        <menu>\n            <item\n                android:id=\"@+id/system_apps\"\n                android:checkable=\"true\"\n                android:title=\"@string/system_apps\" />\n        </menu>\n    </item>\n\n    <item android:title=\"@string/sort\">\n        <menu>\n            <group android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/name\"\n                    android:title=\"@string/name\" />\n                <item\n                    android:id=\"@+id/package_name\"\n                    android:title=\"@string/package_name\" />\n                <item\n                    android:id=\"@+id/install_time\"\n                    android:title=\"@string/install_time\" />\n                <item\n                    android:id=\"@+id/update_time\"\n                    android:title=\"@string/update_time\" />\n            </group>\n\n            <item\n                android:id=\"@+id/reverse\"\n                android:checkable=\"true\"\n                android:title=\"@string/reverse\" />\n        </menu>\n    </item>\n\n    <item android:title=\"@string/external\">\n        <menu>\n            <item\n                android:id=\"@+id/import_from_clipboard\"\n                android:title=\"@string/import_from_clipboard\" />\n            <item\n                android:id=\"@+id/export_to_clipboard\"\n                android:title=\"@string/export_to_clipboard\" />\n        </menu>\n    </item>\n</menu>"
  },
  {
    "path": "design/src/main/res/menu/menu_proxy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:title=\"@string/filter\">\n        <menu>\n            <item\n                android:id=\"@+id/not_selectable\"\n                android:checkable=\"true\"\n                android:title=\"@string/not_selectable\" />\n        </menu>\n    </item>\n    <item android:title=\"@string/mode\">\n        <menu>\n            <group android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/dont_modify\"\n                    android:title=\"@string/dont_modify\" />\n                <item\n                    android:id=\"@+id/direct_mode\"\n                    android:title=\"@string/direct_mode\" />\n                <item\n                    android:id=\"@+id/global_mode\"\n                    android:title=\"@string/global_mode\" />\n                <item\n                    android:id=\"@+id/rule_mode\"\n                    android:title=\"@string/rule_mode\" />\n                <item\n                    android:id=\"@+id/script_mode\"\n                    android:title=\"@string/script_mode\" />\n            </group>\n        </menu>\n    </item>\n    <item android:title=\"@string/layout\">\n        <menu>\n            <group android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/single\"\n                    android:title=\"@string/single\" />\n                <item\n                    android:id=\"@+id/multiple\"\n                    android:title=\"@string/multiple\" />\n            </group>\n        </menu>\n    </item>\n    <item android:title=\"@string/sort\">\n        <menu>\n            <group android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/default_\"\n                    android:title=\"@string/default_\" />\n                <item\n                    android:id=\"@+id/name\"\n                    android:title=\"@string/name\" />\n                <item\n                    android:id=\"@+id/delay\"\n                    android:title=\"@string/delay\" />\n            </group>\n        </menu>\n    </item>\n</menu>"
  },
  {
    "path": "design/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <attr name=\"fullScreenDialogTheme\" format=\"reference\" />\n\n    <attr name=\"colorClashStopped\" format=\"color\" />\n    <attr name=\"colorControlDisabled\" format=\"color\" />\n    <attr name=\"colorLogo\" format=\"color\" />\n\n    <attr name=\"enabled\" format=\"boolean\" />\n    <attr name=\"text\" format=\"string\" />\n    <attr name=\"subtext\" format=\"string\" />\n    <attr name=\"icon\" format=\"reference\" />\n    <attr name=\"color\" format=\"color\" />\n    <attr name=\"placeholder\" format=\"string\" />\n    <attr name=\"title\" format=\"string\" />\n\n    <declare-styleable name=\"LargeActionCard\">\n        <attr name=\"text\" />\n        <attr name=\"subtext\" />\n        <attr name=\"icon\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"LargeActionLabel\">\n        <attr name=\"text\" />\n        <attr name=\"subtext\" />\n        <attr name=\"icon\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ActionLabel\">\n        <attr name=\"enabled\" />\n        <attr name=\"text\" />\n        <attr name=\"subtext\" />\n        <attr name=\"icon\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ActionTextField\">\n        <attr name=\"enabled\" />\n        <attr name=\"icon\" />\n        <attr name=\"title\" />\n        <attr name=\"text\" />\n        <attr name=\"placeholder\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"Clickable\">\n        <attr name=\"android:focusable\" />\n        <attr name=\"android:clickable\" />\n        <attr name=\"android:background\" />\n        <attr name=\"android:foreground\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "design/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"color_clash_light\">#00668B</color>\n    <color name=\"color_clash_dark\">#219BCC</color>\n    <color name=\"color_system_ui_overlay\">#501F1F23</color>\n    <color name=\"color_light_background\">#FAF9FD</color>\n    <color name=\"color_dark_background\">#121316</color>\n    <color name=\"color_dark_surface\">#292A2D</color>\n    <color name=\"color_surface\">#EFEDF1</color>\n    <color name=\"color_light_clash_stopped\">#FF808080</color>\n    <color name=\"color_light_control_disabled\">#FFD3D3D3</color>\n    <color name=\"color_dark_control_disabled\">#FF808080</color>\n    <color name=\"color_error\">#C00003</color>\n</resources>\n"
  },
  {
    "path": "design/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Common -->\n    <dimen name=\"divider_size\">1dp</dimen>\n\n    <!-- Dialog -->\n    <dimen name=\"dialog_padding\">20dp</dimen>\n    <dimen name=\"dialog_button_margin\">5dp</dimen>\n    <dimen name=\"dialog_menu_min_width\">150dp</dimen>\n\n    <!-- Surface -->\n    <dimen name=\"surface_landscape_min_width\">500dp</dimen>\n\n    <!--  Toolbar  -->\n    <dimen name=\"toolbar_height\">155dp</dimen>\n    <dimen name=\"toolbar_elevation\">0dp</dimen>\n    <dimen name=\"toolbar_image_action_padding\">1dp</dimen>\n    <dimen name=\"toolbar_padding_horizontal\">23dp</dimen>\n\n    <!--  Tab Layout  -->\n    <dimen name=\"tab_layout_height\">48dp</dimen>\n    <dimen name=\"tab_layout_elevation\">5dp</dimen>\n\n    <!--  Bottom Sheet  -->\n    <dimen name=\"bottom_sheet_background_padding_top\">25dp</dimen>\n    <dimen name=\"bottom_sheet_header_height\">5dp</dimen>\n    <dimen name=\"bottom_sheet_menu_items_padding\">10dp</dimen>\n\n    <!-- Large Item -->\n    <dimen name=\"large_item_padding_vertical\">15dp</dimen>\n    <dimen name=\"large_item_header_component_size\">30dp</dimen>\n    <dimen name=\"large_item_header_margin_horizontal\">20dp</dimen>\n    <!-- @dimen/large_item_header_component_size + @dimen/large_item_header_margin_horizontal x2 -->\n    <dimen name=\"large_item_header_layout_size\">70dp</dimen>\n    <dimen name=\"large_item_tailing_margin_horizontal\">20dp</dimen>\n    <dimen name=\"large_item_text_margin\">5dp</dimen>\n\n    <!--  Common Item  -->\n    <dimen name=\"item_min_height\">75dp</dimen>\n    <dimen name=\"item_header_component_size\">25dp</dimen>\n    <dimen name=\"item_back_component_size\">18dp</dimen>\n    <dimen name=\"item_header_margin\">25dp</dimen>\n    <dimen name=\"item_tailing_component_size\">21dp</dimen>\n    <dimen name=\"item_tailing_margin\">25dp</dimen><!--17.5-->\n    <dimen name=\"item_padding_vertical\">20dp</dimen><!--正常列表，小标题另外处理-->\n    <dimen name=\"item_sub_padding_vertical\">10dp</dimen>\n    <dimen name=\"item_text_margin\">5dp</dimen>\n    <dimen name=\"item_midden_margin\">10dp</dimen>\n\n    <!-- Large Card -->\n    <dimen name=\"large_action_card_radius\">12dp</dimen>\n    <dimen name=\"large_action_card_elevation\">1dp</dimen>\n    <dimen name=\"large_action_card_min_height\">85dp</dimen>\n\n    <!-- Tips -->\n    <dimen name=\"tips_icon_size\">25dp</dimen>\n    <dimen name=\"tips_icon_margin\">25dp</dimen>\n\n    <!-- Preference -->\n    <dimen name=\"preference_fullscreen_button_padding\">20dp</dimen>\n\n    <!-- Main Design -->\n    <dimen name=\"main_card_margin_vertical\">5dp</dimen>\n    <dimen name=\"main_label_margin_vertical\">2dp</dimen>\n    <dimen name=\"main_padding_horizontal\">23dp</dimen>\n    <dimen name=\"main_logo_size\">50dp</dimen>\n    <dimen name=\"main_top_banner_height\">90dp</dimen>\n\n    <!--  Proxy Design -->\n    <dimen name=\"proxy_layout_padding\">6dp</dimen>\n    <dimen name=\"proxy_content_padding\">22dp</dimen>\n    <dimen name=\"proxy_text_margin\">7dp</dimen>\n    <dimen name=\"proxy_text_size\">13sp</dimen>\n    <dimen name=\"proxy_card_radius\">10dp</dimen>\n    <dimen name=\"proxy_card_offset\">0dp</dimen>\n\n    <!-- Properties Design -->\n    <dimen name=\"properties_element_margin_vertical\">2.5dp</dimen>\n\n    <!-- About Design -->\n    <dimen name=\"about_icon_size\">50dp</dimen>\n    <dimen name=\"about_text_margin\">15dp</dimen>\n\n    <!-- Logcat Design -->\n    <dimen name=\"logcat_padding_vertical\">18dp</dimen>\n    <dimen name=\"logcat_padding_horizontal\">25dp</dimen>\n</resources>"
  },
  {
    "path": "design/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"close_button\" type=\"id\" />\n</resources>"
  },
  {
    "path": "design/src/main/res/values/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"PluralsCandidate\">\n    <string name=\"launch_name\">Clash You</string>\n    <string name=\"application_name\">Clash You</string>\n\n    <string name=\"stopped\">Stopped</string>\n    <string name=\"tap_to_start\">Tap to start</string>\n    <string name=\"running\">Running</string>\n    <string name=\"format_traffic_forwarded\">%s Forwarded</string>\n\n    <string name=\"proxy\">Proxy</string>\n    <string name=\"direct_mode\">Direct Mode</string>\n    <string name=\"rule_mode\">Rule Mode</string>\n    <string name=\"global_mode\">Global Mode</string>\n    <string name=\"script_mode\">Script Mode</string>\n    <string name=\"proxy_empty_tips\">No groups can be displayed</string>\n\n    <string name=\"profiles\">Profiles</string>\n    <string name=\"not_selected\">Not selected</string>\n    <string name=\"format_profile_activated\">%s Activated</string>\n\n    <string name=\"logs\">Logs</string>\n    <string name=\"settings\">Settings</string>\n    <string name=\"help\">Help</string>\n    <string name=\"about\">About</string>\n\n    <string name=\"copied\">Copied</string>\n\n    <string name=\"recently\">Recently</string>\n\n    <string name=\"format_minutes\">%d min</string>\n    <string name=\"format_elements\">%d elements</string>\n\n    <string name=\"format_minutes_ago\">%d min ago</string>\n    <string name=\"format_hours_ago\">%d hours ago</string>\n    <string name=\"format_days_ago\">%d days ago</string>\n    <string name=\"format_months_ago\">%d months ago</string>\n    <string name=\"format_years_ago\">%d years ago</string>\n\n    <string name=\"create_profile\">Create Profile</string>\n    <string name=\"file\">File</string>\n    <string name=\"import_from_file\">Import from File</string>\n    <string name=\"url\">URL</string>\n    <string name=\"import_from_url\">Import from URL</string>\n    <string name=\"external\">External</string>\n    <string name=\"format_type_unsaved\">%s (Unsaved)</string>\n    <string name=\"format_type_saved\">%1$s (%2$s)</string>\n    <string name=\"format_profile_usage\">%1$s / %2$s</string>\n\n    <string name=\"application_broken\">Application Broken</string>\n    <string name=\"application_crashed\">Application Crashed</string>\n    <string name=\"application_broken_tips\">App lacks the necessary runtime components, which is usually caused by downloading an incomplete apk.</string>\n    <string name=\"reinstall\">Reinstall</string>\n    <string name=\"github_releases\">Github Releases</string>\n    <string name=\"github_releases_url\" translatable=\"false\">https://github.com/Yos-X/ClashYou/releases</string>\n\n    <string name=\"profile\">Profile</string>\n    <string name=\"name\">Name</string>\n    <string name=\"profile_name\">Profile Name</string>\n    <string name=\"profile_url\">Profile URL</string>\n    <string name=\"auto_update\">Auto Update</string>\n    <string name=\"auto_update_minutes\">Auto Update (Minutes)</string>\n\n    <string name=\"accept_http_content\">Accept only http(s)</string>\n    <string name=\"at_least_15_minutes\">At least 15 minutes or empty</string>\n    <string name=\"should_not_be_blank\">Should not be blank</string>\n    <string name=\"detail\">Detail</string>\n    <string name=\"update\">Update</string>\n    <string name=\"edit\">Edit</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"export\">Export</string>\n    <string name=\"import_\">Import</string>\n    <string name=\"properties\">Properties</string>\n    <string name=\"duplicate\">Duplicate</string>\n    <string name=\"active_unsaved_tips\">Profile needs to be saved before activation</string>\n\n    <string name=\"exit_without_save\">Exit without Save</string>\n    <string name=\"exit_without_save_warning\">All changed will *LOST*</string>\n\n    <string name=\"raw_cn\" translatable=\"false\">cn</string>\n    <string name=\"disabled\">Disabled</string>\n    <string name=\"unavailable\">Unavailable</string>\n    <string name=\"empty_name\">Empty Name</string>\n    <string name=\"invalid_url\">Invalid URL</string>\n    <string name=\"loading\">Loading</string>\n\n    <string name=\"format_fetching_configuration\">Fetching configuration from \\'%s\\'</string>\n    <string name=\"format_fetching_provider\">Fetching provider \\'%s\\'</string>\n    <string name=\"initializing\">Initializing</string>\n    <string name=\"verifying\">Verifying</string>\n\n    <string name=\"new_profile\">New Profile</string>\n\n    <string name=\"delay\">Delay</string>\n    <string name=\"sort\">Sort</string>\n    <string name=\"default_\">Default</string>\n\n    <string name=\"ok\">OK</string>\n    <string name=\"cancel\">Cancel</string>\n    <string name=\"reset\">Reset</string>\n\n    <string name=\"clash_logcat\">Clash You Logcat</string>\n    <string name=\"history\">History</string>\n    <string name=\"logcat\">Logcat</string>\n\n    <string name=\"delete_all_logs\">Delete All Logs</string>\n    <string name=\"delete_all_logs_warn\">All historical logs will *LOST*</string>\n    <string name=\"file_exported\">File Exported</string>\n\n    <string name=\"behavior\">Behavior</string>\n    <string name=\"network\">Network</string>\n    <string name=\"interface_\">Interface</string>\n    <string name=\"override\">Override</string>\n\n    <string name=\"auto_restart\">Auto Restart</string>\n    <string name=\"allow_clash_auto_restart\">Allow Clash You auto restart</string>\n\n    <string name=\"route_system_traffic\">Route System Traffic</string>\n    <string name=\"routing_via_vpn_service\">Auto routing all system traffic via VpnService</string>\n\n    <string name=\"vpn_service_options\">VpnService Options</string>\n    <string name=\"bypass_private_network\">Bypass Private Network</string>\n    <string name=\"bypass_private_network_summary\">Bypass private network addresses</string>\n    <string name=\"dns_hijacking\">DNS Hijacking</string>\n    <string name=\"dns_hijacking_summary\">Handle all dns packet</string>\n    <string name=\"block_loopback\">Block Loopback</string>\n    <string name=\"block_loopback_summary\">Block loopback connections</string>\n    <string name=\"allow_bypass\">Allow Bypass</string>\n    <string name=\"allow_bypass_summary\">Allows all apps to bypass this VPN connection</string>\n    <string name=\"system_proxy\">System Proxy</string>\n    <string name=\"system_proxy_summary\">Attach http proxy to VpnService</string>\n    <string name=\"access_control_mode\">Access Control Mode</string>\n    <string name=\"access_control_packages\">Access Control Packages</string>\n    <string name=\"access_control_packages_summary\">Configure access permission for apps</string>\n\n    <string name=\"options_unavailable\">Options Unavailable until Clash You Stopped</string>\n\n    <string name=\"general\">General</string>\n    <string name=\"dns\">DNS</string>\n\n    <string name=\"http_port\">HTTP Port</string>\n    <string name=\"socks_port\">Socks Port</string>\n    <string name=\"redirect_port\">Redirect Port</string>\n    <string name=\"mixed_port\">Mixed Port</string>\n    <string name=\"tproxy_port\">TProxy Port</string>\n    <string name=\"authentication\">Authentication</string>\n    <string name=\"allow_lan\">Allow LAN</string>\n    <string name=\"bind_address\">Bind Address</string>\n    <string name=\"mode\">Mode</string>\n    <string name=\"log_level\">Log Level</string>\n    <string name=\"sniff_tls_sni\">Sniff TLS SNI</string>\n    <string name=\"ipv6\">IPv6</string>\n    <string name=\"hosts\">Hosts</string>\n    <string name=\"sideload_geoip\">Sideload GEOIP</string>\n    <string name=\"sideload_geoip_summary\">External GEOIP database</string>\n    <string name=\"_new\">New</string>\n    <string name=\"value\">Value</string>\n\n    <string name=\"strategy\">Strategy</string>\n    <string name=\"listen\">Listen</string>\n    <string name=\"append_system_dns\">Append System DNS</string>\n    <string name=\"use_hosts\">Use Hosts</string>\n    <string name=\"enhanced_mode\">Enhanced Mode</string>\n    <string name=\"name_server\">Name Server</string>\n    <string name=\"fallback\">Fallback Name Server</string>\n    <string name=\"default_name_server\">Default Name Server</string>\n    <string name=\"fakeip_filter\">Fake-IP Filter</string>\n    <string name=\"geoip_fallback\">GeoIP Fallback</string>\n    <string name=\"geoip_fallback_code\">GeoIP Fallback Code</string>\n    <string name=\"ipcidr_fallback\">IPCIDR Fallback</string>\n    <string name=\"domain_fallback\">Domain Fallback</string>\n    <string name=\"name_server_policy\">Name Server Policy</string>\n\n    <string name=\"dont_modify\">Do not modify</string>\n    <string name=\"empty\">Empty</string>\n\n    <string name=\"enabled\">Enabled</string>\n\n    <string name=\"info\">Info</string>\n    <string name=\"warning\">Warning</string>\n    <string name=\"error\">Error</string>\n    <string name=\"debug\">Debug</string>\n    <string name=\"silent\">Silent</string>\n\n    <string name=\"force_enable\">Force Enable</string>\n\n    <string name=\"mapping\">Real-IP to domain mapping</string>\n    <string name=\"fakeip\">Fake-IP to domain mapping</string>\n\n    <string name=\"system_apps\">System Apps</string>\n    <string name=\"filter\">Filter</string>\n    <string name=\"package_name\">Package Name</string>\n    <string name=\"update_time\">Update Time</string>\n    <string name=\"install_time\">Install Time</string>\n    <string name=\"reverse\">Reverse</string>\n    <string name=\"search\">Search</string>\n    <string name=\"select_all\">Select All</string>\n    <string name=\"select_invert\">Select Invert</string>\n    <string name=\"select_none\">Select None</string>\n    <string name=\"import_from_clipboard\">Import from Clipboard</string>\n    <string name=\"export_to_clipboard\">Export to Clipboard</string>\n\n    <string name=\"clash_for_android\">Clash You</string>\n\n    <string name=\"document\">Document</string>\n    <string name=\"feedback\">Feedback</string>\n    <string name=\"sources\">Sources</string>\n    <string name=\"donate\">Donate</string>\n    <string name=\"clash_wiki\">Clash Wiki</string>\n    <string name=\"github_issues\">Github Issues</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"clash_core\">Clash Core</string>\n    <string name=\"clash_wiki_url\" translatable=\"false\">https://github.com/Dreamacro/clash/wiki</string>\n    <string name=\"github_issues_url\" translatable=\"false\">https://github.com/Yos-X/ClashYou/issues</string>\n    <string name=\"donate_url\" translatable=\"false\">https://donate.kr328.app</string>\n    <string name=\"github_url\" translatable=\"false\">https://github.com/Kr328/ClashForAndroid</string>\n    <string name=\"clash_core_url\" translatable=\"false\">https://github.com/Dreamacro/clash</string>\n\n    <string name=\"tips_properties\"><![CDATA[Accept Only <strong>Clash Config</strong>(including <strong>Proxy</strong>/<strong>Rules</strong>)]]></string>\n    <string name=\"tips_help\"><![CDATA[Clash You is a <strong>freeware</strong> and we do <strong>NOT</strong> provide any service for it]]></string>\n\n    <string name=\"google_play_url\" translatable=\"false\">https://play.google.com/store/apps/details?id=com.github.kr238.clash</string>\n\n    <string name=\"app\">App</string>\n    <string name=\"service\">Service</string>\n\n    <string name=\"dark_mode\">Dark Mode</string>\n\n    <string name=\"show_traffic\">Show Traffic</string>\n    <string name=\"show_traffic_summary\">Auto refresh traffic in notification</string>\n\n    <string name=\"follow_system_android_10\">Follow System (Android 10+)</string>\n    <string name=\"always_dark\">Always Dark</string>\n    <string name=\"always_light\">Always Light</string>\n\n    <string name=\"allow_all_apps\">Allow all apps</string>\n    <string name=\"allow_selected_apps\">Allow selected apps</string>\n    <string name=\"deny_selected_apps\">Deny selected apps</string>\n\n    <string name=\"no_profile_selected\">No profile selected</string>\n\n    <string name=\"layout\">Layout</string>\n    <string name=\"single\">Single</string>\n    <string name=\"multiple\">Multiple</string>\n    <string name=\"not_selectable\">Not Selectable</string>\n\n    <string name=\"providers\">Providers</string>\n\n    <string name=\"files\">Files</string>\n    <string name=\"browse_files\">Browse Files</string>\n    <string name=\"browse_configuration_providers\"><![CDATA[Browse configuration & providers]]></string>\n    <string name=\"rename\">Rename</string>\n    <string name=\"file_name\">File Name</string>\n    <string name=\"invalid_file_name\">Invalid file name</string>\n\n    <string name=\"reset_override_settings\">Reset Override Settings</string>\n    <string name=\"reset_override_settings_message\">All override settings will be discarded</string>\n    <string name=\"key\">Key</string>\n\n    <string name=\"use_built_in\">Use Built-in</string>\n    <string name=\"more\">More</string>\n    <string name=\"save\">Save</string>\n    <string name=\"delay_test\">Delay Test</string>\n\n    <string name=\"format_provider_type\">%1$s(%2$s)</string>\n    <string name=\"rule\">Rule</string>\n    <string name=\"http\">HTTP</string>\n    <string name=\"compatible\">Compatible</string>\n    <string name=\"format_update_provider_failure\">Update %1$s: %2$s</string>\n    <string name=\"update_all\">Update All</string>\n\n    <string name=\"close\">Close</string>\n    <string name=\"keyword\">Keyword</string>\n\n    <string name=\"invalid_log_file\">Invalid log file</string>\n    <string name=\"unable_to_start_vpn\">Unable to start VPN component</string>\n\n    <string name=\"request_donate\" />\n    <string name=\"request_donate_tips\" />\n\n    <string name=\"version_updated\">App Updated</string>\n    <string name=\"version_updated_tips\">The settings have been reseted and the old profiles needs to be saved again.</string>\n\n    <string name=\"mode_switch_tips\">Valid only for current session</string>\n    <string name=\"developer_now\">Yos-X（now version）</string>\n    <string name=\"developer_before\">kr238 (original version)</string>\n    <string name=\"donate_url_now\" translatable=\"false\">https://yos-x.github.io</string>\n    <string name=\"permission_request_title\">Permission request</string>\n    <string name=\"permission_notification_desc\">Clash You requires notification permission to display traffic information in the notification bar</string>\n    <string name=\"permission_request_positive\">Go to grant</string>\n    <string name=\"permission_request_negative\">Not now</string>\n    <string name=\"github_url_now\" translatable=\"false\">https://github.com/Yos-X/ClashYou</string>\n    <string name=\"clash_for_android_show\" translatable=\"false\">Clash for Android</string>\n</resources>\n"
  },
  {
    "path": "design/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"ThemeOverlay\" />\n\n    <style name=\"ThemeOverlay.ControlColorError\">\n        <item name=\"colorControlNormal\">@color/color_error</item>\n        <item name=\"android:textColor\">@color/color_error</item>\n    </style>\n</resources>"
  },
  {
    "path": "design/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AppBottomSheetStyle\" parent=\"Theme.Material3.Light.NoActionBar\">\n        <item name=\"android:background\">@drawable/bg_bottom_sheet</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogLightBase\" parent=\"Theme.Material3.Light.NoActionBar\">\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"paddingRightSystemWindowInsets\">false</item>\n        <item name=\"paddingBottomSystemWindowInsets\">false</item>\n        <item name=\"paddingLeftSystemWindowInsets\">false</item>\n\n        <item name=\"colorPrimary\">@color/color_clash_light</item>\n        <item name=\"colorOnPrimary\">@android:color/white</item>\n        <item name=\"colorSurface\">@color/color_surface</item>\n        <item name=\"colorControlNormal\">@android:color/black</item>\n        <item name=\"colorClashStopped\">@color/color_light_clash_stopped</item>\n        <item name=\"colorControlDisabled\">@color/color_light_control_disabled</item>\n\n        <item name=\"colorPrimaryDark\">?attr/colorPrimary</item>\n        <item name=\"colorSecondary\">?attr/colorPrimary</item>\n        <item name=\"colorOnSecondary\">?attr/colorOnPrimary</item>\n        <item name=\"colorControlActivated\">?attr/colorPrimary</item>\n        <item name=\"android:textColor\">?attr/colorControlNormal</item>\n        <item name=\"android:windowBackground\">@color/color_light_background</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogDarkBase\" parent=\"Theme.Material3.DayNight.NoActionBar\">\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"paddingRightSystemWindowInsets\">false</item>\n        <item name=\"paddingBottomSystemWindowInsets\">false</item>\n        <item name=\"paddingLeftSystemWindowInsets\">false</item>\n\n        <item name=\"colorPrimary\">@color/color_clash_dark</item>\n        <item name=\"colorOnPrimary\">@android:color/white</item>\n        <item name=\"colorSurface\">@color/color_dark_surface</item>\n        <item name=\"colorControlDisabled\">@color/color_dark_control_disabled</item>\n        <item name=\"android:windowBackground\">@color/color_dark_background</item>\n\n        <item name=\"colorPrimaryDark\">?attr/colorPrimary</item>\n        <item name=\"colorSecondary\">?attr/colorPrimary</item>\n        <item name=\"colorOnSecondary\">?attr/colorOnPrimary</item>\n        <item name=\"colorControlActivated\">?attr/colorPrimary</item>\n        <item name=\"colorClashStopped\">?attr/colorSurface</item>\n        <item name=\"android:textColor\">?attr/colorControlNormal</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogLight\" parent=\"AppFullScreenDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@color/color_system_ui_overlay</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogDark\" parent=\"AppFullScreenDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogLightBase\" parent=\"Theme.MaterialComponents.Light.BottomSheetDialog\">\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"paddingRightSystemWindowInsets\">false</item>\n        <item name=\"paddingBottomSystemWindowInsets\">false</item>\n        <item name=\"paddingLeftSystemWindowInsets\">false</item>\n\n        <item name=\"colorPrimary\">@color/color_clash_light</item>\n        <item name=\"colorOnPrimary\">@android:color/white</item>\n        <item name=\"colorSurface\">@color/color_surface</item>\n        <item name=\"colorControlNormal\">@android:color/black</item>\n        <item name=\"colorClashStopped\">@color/color_light_clash_stopped</item>\n        <item name=\"colorControlDisabled\">@color/color_light_control_disabled</item>\n\n        <item name=\"colorPrimaryDark\">?attr/colorPrimary</item>\n        <item name=\"colorSecondary\">?attr/colorPrimary</item>\n        <item name=\"colorOnSecondary\">?attr/colorOnPrimary</item>\n        <item name=\"colorControlActivated\">?attr/colorPrimary</item>\n        <item name=\"android:textColor\">?attr/colorControlNormal</item>\n\n        <item name=\"bottomSheetStyle\">@style/AppBottomSheetStyle</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogDarkBase\" parent=\"Theme.MaterialComponents.BottomSheetDialog\">\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"paddingRightSystemWindowInsets\">false</item>\n        <item name=\"paddingBottomSystemWindowInsets\">false</item>\n        <item name=\"paddingLeftSystemWindowInsets\">false</item>\n\n        <item name=\"colorPrimary\">@color/color_clash_dark</item>\n        <item name=\"colorOnPrimary\">@android:color/white</item>\n        <item name=\"colorSurface\">@color/color_dark_surface</item>\n        <item name=\"colorControlDisabled\">@color/color_dark_control_disabled</item>\n\n        <item name=\"colorPrimaryDark\">?attr/colorPrimary</item>\n        <item name=\"colorSecondary\">?attr/colorPrimary</item>\n        <item name=\"colorOnSecondary\">?attr/colorOnPrimary</item>\n        <item name=\"colorControlActivated\">?attr/colorPrimary</item>\n        <item name=\"colorClashStopped\">?attr/colorSurface</item>\n        <item name=\"android:textColor\">?attr/colorControlNormal</item>\n\n        <item name=\"bottomSheetStyle\">@style/AppBottomSheetStyle</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogLight\" parent=\"AppBottomSheetDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@color/color_system_ui_overlay</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogDark\" parent=\"AppBottomSheetDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"AppThemeLightBase\" parent=\"Theme.Material3.Light.NoActionBar\">\n        <item name=\"android:windowBackground\">@color/color_light_background</item>\n        <item name=\"android:ambientShadowAlpha\">0.08</item>\n        <item name=\"android:spotShadowAlpha\">0.08</item>\n\n        <item name=\"colorPrimary\">@color/color_clash_light</item>\n        <item name=\"colorOnPrimary\">@android:color/white</item>\n        <item name=\"colorSurface\">@color/color_surface</item>\n        <item name=\"colorControlNormal\">@android:color/black</item>\n        <item name=\"colorClashStopped\">@color/color_light_clash_stopped</item>\n        <item name=\"colorControlDisabled\">@color/color_light_control_disabled</item>\n\n        <item name=\"colorPrimaryDark\">?attr/colorPrimary</item>\n        <item name=\"colorSecondary\">?attr/colorPrimary</item>\n        <item name=\"colorOnSecondary\">?attr/colorOnPrimary</item>\n        <item name=\"colorControlActivated\">?attr/colorPrimary</item>\n        <item name=\"colorLogo\">?attr/colorControlActivated</item>\n        <item name=\"android:textColor\">?attr/colorControlNormal</item>\n\n        <item name=\"bottomSheetDialogTheme\">@style/AppBottomSheetDialogLight</item>\n        <item name=\"fullScreenDialogTheme\">@style/AppFullScreenDialogLight</item>\n    </style>\n\n    <style name=\"AppThemeDarkBase\" parent=\"Theme.Material3.DayNight.NoActionBar\">\n        <item name=\"android:windowBackground\">@color/color_dark_background</item>\n        <item name=\"android:ambientShadowAlpha\">0.08</item>\n        <item name=\"android:spotShadowAlpha\">0.08</item>\n\n        <item name=\"colorPrimary\">@color/color_clash_dark</item>\n        <item name=\"colorOnPrimary\">@android:color/white</item>\n        <item name=\"colorSurface\">@color/color_dark_surface</item>\n        <item name=\"colorControlNormal\">@android:color/white</item>\n        <item name=\"colorControlDisabled\">@color/color_dark_control_disabled</item>\n\n        <item name=\"colorPrimaryDark\">?attr/colorPrimary</item>\n        <item name=\"colorSecondary\">?attr/colorPrimary</item>\n        <item name=\"colorOnSecondary\">?attr/colorOnPrimary</item>\n        <item name=\"colorControlActivated\">?attr/colorPrimary</item>\n        <item name=\"colorClashStopped\">?attr/colorSurface</item>\n        <item name=\"colorLogo\">?attr/colorControlNormal</item>\n        <item name=\"android:textColor\">?attr/colorControlNormal</item>\n\n        <item name=\"bottomSheetDialogTheme\">@style/AppBottomSheetDialogDark</item>\n        <item name=\"fullScreenDialogTheme\">@style/AppFullScreenDialogDark</item>\n    </style>\n\n    <style name=\"AppThemeLight\" parent=\"AppThemeLightBase\">\n        <item name=\"android:statusBarColor\">@color/color_system_ui_overlay</item>\n        <item name=\"android:navigationBarColor\">@color/color_system_ui_overlay</item>\n    </style>\n\n    <style name=\"AppThemeDark\" parent=\"AppThemeDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n    </style>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-v23/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AppFullScreenDialogLight\" parent=\"AppFullScreenDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@color/color_system_ui_overlay</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogDark\" parent=\"AppFullScreenDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogLight\" parent=\"AppBottomSheetDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@color/color_system_ui_overlay</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogDark\" parent=\"AppBottomSheetDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n    </style>\n\n    <style name=\"AppThemeLight\" parent=\"AppThemeLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@color/color_system_ui_overlay</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n    </style>\n\n    <style name=\"AppThemeDark\" parent=\"AppThemeDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n    </style>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-v27/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AppFullScreenDialogLight\" parent=\"AppFullScreenDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarDividerColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogDark\" parent=\"AppFullScreenDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarDividerColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n        <item name=\"android:windowLightNavigationBar\">false</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogLight\" parent=\"AppBottomSheetDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarDividerColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogDark\" parent=\"AppBottomSheetDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarDividerColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n        <item name=\"android:windowLightNavigationBar\">false</item>\n    </style>\n\n    <style name=\"AppThemeLight\" parent=\"AppThemeLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarDividerColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n    </style>\n\n    <style name=\"AppThemeDark\" parent=\"AppThemeDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarDividerColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n        <item name=\"android:windowLightNavigationBar\">false</item>\n    </style>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-v29/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AppFullScreenDialogLight\" parent=\"AppFullScreenDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n        <item name=\"android:enforceStatusBarContrast\">false</item>\n        <item name=\"android:enforceNavigationBarContrast\">false</item>\n    </style>\n\n    <style name=\"AppFullScreenDialogDark\" parent=\"AppFullScreenDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n        <item name=\"android:windowLightNavigationBar\">false</item>\n        <item name=\"android:enforceStatusBarContrast\">false</item>\n        <item name=\"android:enforceNavigationBarContrast\">false</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogLight\" parent=\"AppBottomSheetDialogLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n        <item name=\"android:enforceStatusBarContrast\">false</item>\n        <item name=\"android:enforceNavigationBarContrast\">false</item>\n    </style>\n\n    <style name=\"AppBottomSheetDialogDark\" parent=\"AppBottomSheetDialogDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n        <item name=\"android:windowLightNavigationBar\">false</item>\n        <item name=\"android:enforceStatusBarContrast\">false</item>\n        <item name=\"android:enforceNavigationBarContrast\">false</item>\n    </style>\n\n    <style name=\"AppThemeLight\" parent=\"AppThemeLightBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n        <item name=\"android:enforceStatusBarContrast\">false</item>\n        <item name=\"android:enforceNavigationBarContrast\">false</item>\n    </style>\n\n    <style name=\"AppThemeDark\" parent=\"AppThemeDarkBase\">\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">false</item>\n        <item name=\"android:windowLightNavigationBar\">false</item>\n        <item name=\"android:enforceStatusBarContrast\">false</item>\n        <item name=\"android:enforceNavigationBarContrast\">false</item>\n    </style>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-v31/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"color_clash_light\">@android:color/system_accent1_600</color>\n    <color name=\"color_clash_dark\">@android:color/system_accent1_400</color>\n    <color name=\"color_system_ui_overlay\">#501F1F23</color>\n    <color name=\"color_light_background\">#FAF9FD</color>\n    <color name=\"color_dark_background\">#121316</color>\n    <color name=\"color_surface\">#EFEDF1</color>\n    <color name=\"color_dark_surface\">#292A2D</color>\n    <color name=\"color_light_clash_stopped\">#FF808080</color>\n    <color name=\"color_light_control_disabled\">#FFD3D3D3</color>\n    <color name=\"color_dark_control_disabled\">#FF808080</color>\n    <color name=\"color_error\">#C00003</color>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-v34/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"color_error\">@android:color/system_error_light</color>\n    <color name=\"color_clash_light\">@android:color/system_primary_light</color>\n    <color name=\"color_clash_dark\">@android:color/system_primary_dark</color>\n    <color name=\"color_system_ui_overlay\">@android:color/system_surface_container_dark</color>\n    <color name=\"color_light_background\">@android:color/system_background_light</color>\n    <color name=\"color_dark_background\">@android:color/system_background_dark</color>\n    <color name=\"color_dark_surface\">@android:color/system_surface_container_high_dark</color>\n    <color name=\"color_light_clash_stopped\">#FF808080</color>\n    <color name=\"color_light_control_disabled\">#FFD3D3D3</color>\n    <color name=\"color_dark_control_disabled\">#FF808080</color>\n    <color name=\"color_surface\">@android:color/system_surface_container_light</color>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"access_control_packages_summary\">为应用配置访问权限</string>\n    <string name=\"about\">关于</string>\n    <string name=\"access_control_mode\">访问控制模式</string>\n    <string name=\"access_control_packages\">访问控制应用包列表</string>\n    <string name=\"append_system_dns\">追加系统 DNS</string>\n    <string name=\"application_broken\">应用损坏</string>\n    <string name=\"application_name\">Clash You</string>\n    <string name=\"auto_update\">自动更新</string>\n    <string name=\"behavior\">行为</string>\n    <string name=\"bypass_private_network\">绕过私有网络</string>\n    <string name=\"bypass_private_network_summary\">绕过私有网络地址</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"clash_logcat\">Clash You 日志捕捉工具</string>\n    <string name=\"create_profile\">创建配置</string>\n    <string name=\"dark_mode\">暗黑模式</string>\n    <string name=\"default_\">默认</string>\n    <string name=\"delay\">延迟</string>\n    <string name=\"delete\">删除</string>\n    <string name=\"delete_all_logs\">删除所有日志</string>\n    <string name=\"delete_all_logs_warn\">所有历史日志将丢失</string>\n    <string name=\"detail\">详情</string>\n    <string name=\"direct_mode\">直连模式</string>\n    <string name=\"disabled\">已禁用</string>\n    <string name=\"dns_hijacking\">DNS 劫持</string>\n    <string name=\"dns_hijacking_summary\">处理所有 DNS 数据包</string>\n    <string name=\"duplicate\">复制</string>\n    <string name=\"edit\">编辑</string>\n    <string name=\"empty_name\">空名称</string>\n    <string name=\"exit_without_save\">退出而不保存</string>\n    <string name=\"exit_without_save_warning\">所有变更将会丢失</string>\n    <string name=\"export\">导出</string>\n    <string name=\"external\">外部</string>\n    <string name=\"file\">文件</string>\n    <string name=\"file_exported\">文件已导出</string>\n    <string name=\"format_minutes\">%d 分钟</string>\n    <string name=\"format_profile_activated\">%s 已激活</string>\n    <string name=\"format_traffic_forwarded\">%s 已转发</string>\n    <string name=\"global_mode\">全局模式</string>\n    <string name=\"history\">历史</string>\n    <string name=\"import_from_file\">从文件导入</string>\n    <string name=\"import_from_url\">从 URL 导入</string>\n    <string name=\"interface_\">界面</string>\n    <string name=\"invalid_url\">无效的 URL</string>\n    <string name=\"launch_name\">Clash You</string>\n    <string name=\"logcat\">Logcat</string>\n    <string name=\"logs\">日志</string>\n    <string name=\"mode\">模式</string>\n    <string name=\"name\">名称</string>\n    <string name=\"network\">网络</string>\n    <string name=\"new_profile\">新配置</string>\n    <string name=\"not_selected\">未选择</string>\n    <string name=\"ok\">确认</string>\n    <string name=\"profile\">配置</string>\n    <string name=\"profile_name\">配置名称</string>\n    <string name=\"profiles\">配置</string>\n    <string name=\"properties\">参数</string>\n    <string name=\"proxy\">代理</string>\n    <string name=\"recently\">近期</string>\n    <string name=\"route_system_traffic\">自动路由系统流量</string>\n    <string name=\"routing_via_vpn_service\">通过 VpnService 自动路由所有系统流量</string>\n    <string name=\"rule_mode\">规则模式</string>\n    <string name=\"running\">运行中</string>\n    <string name=\"settings\">设置</string>\n    <string name=\"show_traffic\">显示流量</string>\n    <string name=\"show_traffic_summary\">在通知中自动刷新流量</string>\n    <string name=\"allow_clash_auto_restart\">允许 Clash You 自动重启</string>\n    <string name=\"auto_restart\">自动重启</string>\n    <string name=\"stopped\">已停止</string>\n    <string name=\"help\">帮助</string>\n    <string name=\"tap_to_start\">点此启动</string>\n    <string name=\"update\">更新</string>\n    <string name=\"url\">URL</string>\n    <string name=\"vpn_service_options\">VpnService 选项</string>\n    <string name=\"options_unavailable\">选项在 Clash You 运行时不可用</string>\n    <string name=\"search\">查找</string>\n    <string name=\"system_apps\">系统应用</string>\n    <string name=\"update_time\">更新时间</string>\n    <string name=\"package_name\">应用包名称</string>\n    <string name=\"install_time\">安装时间</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"feedback\">反馈</string>\n    <string name=\"github_issues\">Github Issues</string>\n    <string name=\"tips_properties\"><![CDATA[仅接受 <strong>Clash 配置文件</strong>(包含<strong>代理</strong>/<strong>规则</strong>)]]></string>\n    <string name=\"loading\">载入中</string>\n    <string name=\"tips_help\"><![CDATA[Clash You 是一个<strong>免费软件</strong>并且我们<strong>不</strong>为其提供任何服务, <strong>请务必不要反馈非应用自身引起的问题</strong>]]></string>\n    <string name=\"donate\">捐赠</string>\n    <string name=\"allow_all_apps\">允许所有应用</string>\n    <string name=\"allow_selected_apps\">仅允许已选择的应用</string>\n    <string name=\"deny_selected_apps\">不允许已选择的应用</string>\n    <string name=\"no_profile_selected\">没有选择配置文件</string>\n    <string name=\"copied\">已复制</string>\n    <string name=\"script_mode\">脚本模式</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"filter\">过滤</string>\n    <string name=\"select_all\">全选</string>\n    <string name=\"select_invert\">反选</string>\n    <string name=\"select_none\">清除</string>\n    <string name=\"app\">应用</string>\n    <string name=\"follow_system_android_10\">跟随系统 (Android 10+)</string>\n    <string name=\"always_dark\">总是暗黑模式</string>\n    <string name=\"always_light\">总是明亮模式</string>\n    <string name=\"service\">服务</string>\n    <string name=\"accept_http_content\">仅接受 http(s) 和 content 类型</string>\n    <string name=\"at_least_15_minutes\">至少 15 分钟</string>\n    <string name=\"override\">覆写</string>\n    <string name=\"general\">常规</string>\n    <string name=\"dns\">DNS</string>\n    <string name=\"http_port\">HTTP 端口</string>\n    <string name=\"socks_port\">Socks 端口</string>\n    <string name=\"mixed_port\">复合端口</string>\n    <string name=\"allow_lan\">允许来自局域网的连接</string>\n    <string name=\"bind_address\">监听地址</string>\n    <string name=\"log_level\">日志级别</string>\n    <string name=\"ipv6\">IPv6</string>\n    <string name=\"hosts\">Hosts</string>\n    <string name=\"enabled\">已启用</string>\n    <string name=\"info\">消息</string>\n    <string name=\"warning\">警告</string>\n    <string name=\"error\">错误</string>\n    <string name=\"debug\">调试</string>\n    <string name=\"silent\">停用</string>\n    <string name=\"format_elements\">%d 个条目</string>\n    <string name=\"strategy\">策略</string>\n    <string name=\"enhanced_mode\">增强模式</string>\n    <string name=\"name_server\">Name Server</string>\n    <string name=\"fallback\">Fallback Name Server</string>\n    <string name=\"default_name_server\">Default Name Server</string>\n    <string name=\"fakeip_filter\">FakeIP 过滤器</string>\n    <string name=\"geoip_fallback\">GeoIP Fallback</string>\n    <string name=\"ipcidr_fallback\">IPCIDR Fallback</string>\n    <string name=\"use_built_in\">使用内置</string>\n    <string name=\"mapping\">Real-IP 至 域名映射</string>\n    <string name=\"fakeip\">Fake-IP 至 域名映射</string>\n    <string name=\"sort\">排序</string>\n    <string name=\"layout\">布局</string>\n    <string name=\"single\">单列</string>\n    <string name=\"multiple\">多列</string>\n    <string name=\"not_selectable\">不可选择</string>\n    <string name=\"providers\">外部资源</string>\n    <string name=\"unavailable\">不可用</string>\n    <string name=\"_new\">新建</string>\n    <string name=\"value\">值</string>\n    <string name=\"listen\">监听</string>\n    <string name=\"files\">文件</string>\n    <string name=\"browse_files\">浏览文件</string>\n    <string name=\"browse_configuration_providers\">浏览配置文件和外部资源</string>\n    <string name=\"rename\">重命名</string>\n    <string name=\"file_name\">文件名</string>\n    <string name=\"format_type_unsaved\">%s (未保存)</string>\n    <string name=\"format_type_saved\">%1$s (%2$s)</string>\n    <string name=\"format_profile_usage\">%1$s / %2$s</string>\n    <string name=\"format_minutes_ago\">%d 分钟前</string>\n    <string name=\"format_hours_ago\">%d 小时前</string>\n    <string name=\"format_days_ago\">%d 天前</string>\n    <string name=\"format_months_ago\">%d 月前</string>\n    <string name=\"format_years_ago\">%d 年前</string>\n    <string name=\"system_proxy\">系统代理</string>\n    <string name=\"system_proxy_summary\">为 VpnService 附加 HTTP 代理</string>\n    <string name=\"dont_modify\">不修改</string>\n    <string name=\"redirect_port\">Redirect 端口</string>\n    <string name=\"tproxy_port\">TProxy 端口</string>\n    <string name=\"reset\">重置</string>\n    <string name=\"use_hosts\">使用 Hosts</string>\n    <string name=\"authentication\">认证</string>\n    <string name=\"domain_fallback\">域名 Fallback</string>\n    <string name=\"empty\">置空</string>\n    <string name=\"import_from_clipboard\">从剪切板导入</string>\n    <string name=\"export_to_clipboard\">导出至剪切板</string>\n    <string name=\"auto_update_minutes\">自动更新 (分钟)</string>\n    <string name=\"profile_url\">配置 URL</string>\n    <string name=\"should_not_be_blank\">不能为空</string>\n    <string name=\"format_fetching_configuration\">正在从 %s 下载配置文件</string>\n    <string name=\"format_fetching_provider\">正在下载外部资源 %s</string>\n    <string name=\"initializing\">正在初始化</string>\n    <string name=\"verifying\">正在校验</string>\n    <string name=\"sideload_geoip\">旁加载 GEOIP</string>\n    <string name=\"sideload_geoip_summary\">外部 GEOIP 数据库</string>\n    <string name=\"force_enable\">强制启用</string>\n    <string name=\"document\">文档</string>\n    <string name=\"clash_wiki\">Clash Wiki</string>\n    <string name=\"invalid_file_name\">非法的文件名称</string>\n    <string name=\"reset_override_settings\">重置覆写设置</string>\n    <string name=\"reset_override_settings_message\">所有的覆写设置将会被擦除</string>\n    <string name=\"key\">键</string>\n    <string name=\"more\">更多</string>\n    <string name=\"save\">保存</string>\n    <string name=\"delay_test\">延迟测试</string>\n    <string name=\"format_provider_type\">%1$s(%2$s)</string>\n    <string name=\"rule\">规则</string>\n    <string name=\"http\">HTTP</string>\n    <string name=\"compatible\">兼容</string>\n    <string name=\"format_update_provider_failure\">更新 %1$s: %2$s</string>\n    <string name=\"update_all\">更新全部</string>\n    <string name=\"proxy_empty_tips\">没有可以显示的组</string>\n    <string name=\"reverse\">反转</string>\n    <string name=\"close\">关闭</string>\n    <string name=\"keyword\">关键词</string>\n    <string name=\"invalid_log_file\">无效的日志文件</string>\n    <string name=\"application_crashed\">应用崩溃</string>\n    <string name=\"application_broken_tips\">应用缺少必要的运行组件，这通常由于下载不完整的 apk 导致。</string>\n    <string name=\"reinstall\">重新安装</string>\n    <string name=\"github_releases\">Github Releases</string>\n    <string name=\"unable_to_start_vpn\">无法启动 VPN 组件</string>\n    <string name=\"request_donate_tips\">如果您觉得本应用对您有帮助欢迎在 [帮助] 中给予开发者一点捐赠</string>\n    <string name=\"request_donate\">捐赠</string>\n    <string name=\"version_updated\">应用已更新</string>\n    <string name=\"version_updated_tips\">设置已被清除，旧的配置文件需要再次保存。</string>\n    <string name=\"active_unsaved_tips\">配置文件需要在激活之前保存</string>\n    <string name=\"mode_switch_tips\">仅在本次会话中有效</string>\n    <string name=\"import_\">导入</string>\n    <string name=\"sources\">源代码</string>\n    <string name=\"clash_core\">Clash 核心</string>\n    <string name=\"name_server_policy\">Name Server 策略</string>\n    <string name=\"block_loopback\">阻止本地回环</string>\n    <string name=\"block_loopback_summary\">阻止本地回环连接</string>\n    <string name=\"geoip_fallback_code\">GeoIP Fallback 区域代码</string>\n    <string name=\"allow_bypass\">允许应用绕过</string>\n    <string name=\"allow_bypass_summary\">允许其他应用绕过 VPN</string>\n    <string name=\"sniff_tls_sni\">嗅探 TLS 的 SNI</string>\n    <string name=\"developer_now\">枫灵剑影（当前版本）</string>\n    <string name=\"developer_before\">kr238（原始版本）</string>\n    <string name=\"permission_request_title\">权限请求</string>\n    <string name=\"permission_notification_desc\">Clash You 需要通知权限以在通知栏显示流量信息</string>\n    <string name=\"permission_request_positive\">前往授予</string>\n    <string name=\"permission_request_negative\">暂时不用</string>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-zh-rHK/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"access_control_packages_summary\">為應用配置訪問權限</string>\n    <string name=\"about\">關於</string>\n    <string name=\"access_control_mode\">訪問控制模式</string>\n    <string name=\"access_control_packages\">訪問控制應用包列表</string>\n    <string name=\"append_system_dns\">追加系統 DNS</string>\n    <string name=\"application_broken\">應用損壞</string>\n    <string name=\"application_name\">Clash You</string>\n    <string name=\"auto_update\">自動更新</string>\n    <string name=\"behavior\">行為</string>\n    <string name=\"bypass_private_network\">繞過私有網絡</string>\n    <string name=\"bypass_private_network_summary\">繞過私有網絡地址</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"clash_logcat\">Clash You 日誌捕捉工具</string>\n    <string name=\"create_profile\">創建配置</string>\n    <string name=\"dark_mode\">暗黑模式</string>\n    <string name=\"default_\">默認</string>\n    <string name=\"delay\">延遲</string>\n    <string name=\"delete\">刪除</string>\n    <string name=\"delete_all_logs\">刪除所有日誌</string>\n    <string name=\"delete_all_logs_warn\">所有歷史日誌將丟失</string>\n    <string name=\"detail\">詳情</string>\n    <string name=\"direct_mode\">直連模式</string>\n    <string name=\"disabled\">已禁用</string>\n    <string name=\"dns_hijacking\">DNS 劫持</string>\n    <string name=\"dns_hijacking_summary\">處理所有 DNS 數據包</string>\n    <string name=\"duplicate\">複製</string>\n    <string name=\"edit\">編輯</string>\n    <string name=\"empty_name\">空名稱</string>\n    <string name=\"exit_without_save\">退出而不保存</string>\n    <string name=\"exit_without_save_warning\">所有變更將會丟失</string>\n    <string name=\"export\">導出</string>\n    <string name=\"external\">外部</string>\n    <string name=\"file\">文件</string>\n    <string name=\"file_exported\">文件已導出</string>\n    <string name=\"format_minutes\">%d 分鐘</string>\n    <string name=\"format_profile_activated\">%s 已激活</string>\n    <string name=\"format_traffic_forwarded\">%s 已轉發</string>\n    <string name=\"global_mode\">全局模式</string>\n    <string name=\"history\">歷史</string>\n    <string name=\"import_from_file\">從文件導入</string>\n    <string name=\"import_from_url\">從 URL 導入</string>\n    <string name=\"interface_\">界面</string>\n    <string name=\"invalid_url\">無效的 URL</string>\n    <string name=\"launch_name\">Clash You</string>\n    <string name=\"logcat\">Logcat</string>\n    <string name=\"logs\">日誌</string>\n    <string name=\"mode\">模式</string>\n    <string name=\"name\">名稱</string>\n    <string name=\"network\">網絡</string>\n    <string name=\"new_profile\">新配置</string>\n    <string name=\"not_selected\">未選擇</string>\n    <string name=\"ok\">確認</string>\n    <string name=\"profile\">配置</string>\n    <string name=\"profile_name\">配置名稱</string>\n    <string name=\"profiles\">配置</string>\n    <string name=\"properties\">參數</string>\n    <string name=\"proxy\">代理</string>\n    <string name=\"recently\">近期</string>\n    <string name=\"route_system_traffic\">自動路由系統流量</string>\n    <string name=\"routing_via_vpn_service\">通過 VpnService 自動路由所有系統流量</string>\n    <string name=\"rule_mode\">規則模式</string>\n    <string name=\"running\">運行中</string>\n    <string name=\"settings\">設置</string>\n    <string name=\"show_traffic\">顯示流量</string>\n    <string name=\"show_traffic_summary\">在通知中自動刷新流量</string>\n    <string name=\"allow_clash_auto_restart\">允許 Clash You 自動重啓</string>\n    <string name=\"auto_restart\">自動重啓</string>\n    <string name=\"stopped\">已停止</string>\n    <string name=\"help\">幫助</string>\n    <string name=\"tap_to_start\">點此啓動</string>\n    <string name=\"update\">更新</string>\n    <string name=\"url\">URL</string>\n    <string name=\"vpn_service_options\">VpnService 選項</string>\n    <string name=\"options_unavailable\">選項在 Clash You 運行時不可用</string>\n    <string name=\"search\">查找</string>\n    <string name=\"system_apps\">系統應用</string>\n    <string name=\"update_time\">更新時間</string>\n    <string name=\"package_name\">應用包名稱</string>\n    <string name=\"install_time\">安裝時間</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"feedback\">反饋</string>\n    <string name=\"github_issues\">Github Issues</string>\n    <string name=\"tips_properties\"><![CDATA[僅接受 <strong>Clash 配置文件</strong>(包含<strong>代理</strong>/<strong>規則</strong>)]]></string>\n    <string name=\"loading\">載入中</string>\n    <string name=\"tips_help\"><![CDATA[Clash You 是一個<strong>免費軟件</strong>並且我們<strong>不</strong>為其提供任何服務, <strong>請務必不要反饋非應用自身引起的問題</strong>]]></string>\n    <string name=\"donate\">捐贈</string>\n    <string name=\"allow_all_apps\">允許所有應用</string>\n    <string name=\"allow_selected_apps\">僅允許已選擇的應用</string>\n    <string name=\"deny_selected_apps\">不允許已選擇的應用</string>\n    <string name=\"no_profile_selected\">沒有選擇配置文件</string>\n    <string name=\"copied\">已複製</string>\n    <string name=\"script_mode\">腳本模式</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"filter\">過濾</string>\n    <string name=\"select_all\">全選</string>\n    <string name=\"select_invert\">反選</string>\n    <string name=\"select_none\">清除</string>\n    <string name=\"app\">應用</string>\n    <string name=\"follow_system_android_10\">跟隨系統 (Android 10+)</string>\n    <string name=\"always_dark\">總是暗黑模式</string>\n    <string name=\"always_light\">總是明亮模式</string>\n    <string name=\"service\">服務</string>\n    <string name=\"accept_http_content\">僅接受 http(s) 和 content 類型</string>\n    <string name=\"at_least_15_minutes\">至少 15 分鐘</string>\n    <string name=\"override\">覆寫</string>\n    <string name=\"general\">常規</string>\n    <string name=\"dns\">DNS</string>\n    <string name=\"http_port\">HTTP 端口</string>\n    <string name=\"socks_port\">Socks 端口</string>\n    <string name=\"mixed_port\">複合端口</string>\n    <string name=\"allow_lan\">允許來自局域網的連接</string>\n    <string name=\"bind_address\">監聽地址</string>\n    <string name=\"log_level\">日誌級別</string>\n    <string name=\"ipv6\">IPv6</string>\n    <string name=\"hosts\">Hosts</string>\n    <string name=\"enabled\">已啓用</string>\n    <string name=\"info\">消息</string>\n    <string name=\"warning\">警告</string>\n    <string name=\"error\">錯誤</string>\n    <string name=\"debug\">調試</string>\n    <string name=\"silent\">停用</string>\n    <string name=\"format_elements\">%d 個條目</string>\n    <string name=\"strategy\">策略</string>\n    <string name=\"enhanced_mode\">增強模式</string>\n    <string name=\"name_server\">Name Server</string>\n    <string name=\"fallback\">Fallback Name Server</string>\n    <string name=\"default_name_server\">Default Name Server</string>\n    <string name=\"fakeip_filter\">FakeIP 過濾器</string>\n    <string name=\"geoip_fallback\">GeoIP Fallback</string>\n    <string name=\"ipcidr_fallback\">IPCIDR Fallback</string>\n    <string name=\"use_built_in\">使用內置</string>\n    <string name=\"mapping\">Real-IP 至 域名映射</string>\n    <string name=\"fakeip\">Fake-IP 至 域名映射</string>\n    <string name=\"sort\">排序</string>\n    <string name=\"layout\">佈局</string>\n    <string name=\"single\">單列</string>\n    <string name=\"multiple\">多列</string>\n    <string name=\"not_selectable\">不可選擇</string>\n    <string name=\"providers\">外部資源</string>\n    <string name=\"unavailable\">不可用</string>\n    <string name=\"_new\">新建</string>\n    <string name=\"value\">值</string>\n    <string name=\"listen\">監聽</string>\n    <string name=\"files\">文件</string>\n    <string name=\"browse_files\">瀏覽文件</string>\n    <string name=\"browse_configuration_providers\">瀏覽配置文件和外部資源</string>\n    <string name=\"rename\">重命名</string>\n    <string name=\"file_name\">文件名</string>\n    <string name=\"format_type_unsaved\">%s (未保存)</string>\n    <string name=\"format_type_saved\">%1$s (%2$s)</string>\n    <string name=\"format_profile_usage\">%1$s / %2$s</string>\n    <string name=\"format_minutes_ago\">%d 分鐘前</string>\n    <string name=\"format_hours_ago\">%d 小時前</string>\n    <string name=\"format_days_ago\">%d 天前</string>\n    <string name=\"format_months_ago\">%d 月前</string>\n    <string name=\"format_years_ago\">%d 年前</string>\n    <string name=\"system_proxy\">系統代理</string>\n    <string name=\"system_proxy_summary\">為 VpnService 附加 HTTP 代理</string>\n    <string name=\"dont_modify\">不修改</string>\n    <string name=\"redirect_port\">Redirect 端口</string>\n    <string name=\"tproxy_port\">TProxy 端口</string>\n    <string name=\"reset\">重置</string>\n    <string name=\"use_hosts\">使用 Hosts</string>\n    <string name=\"authentication\">認證</string>\n    <string name=\"domain_fallback\">域名 Fallback</string>\n    <string name=\"empty\">置空</string>\n    <string name=\"import_from_clipboard\">從剪切板導入</string>\n    <string name=\"export_to_clipboard\">導出至剪切板</string>\n    <string name=\"auto_update_minutes\">自動更新 (分鐘)</string>\n    <string name=\"profile_url\">配置 URL</string>\n    <string name=\"should_not_be_blank\">不能為空</string>\n    <string name=\"format_fetching_configuration\">正在從 %s 下載配置文件</string>\n    <string name=\"format_fetching_provider\">正在下載外部資源 %s</string>\n    <string name=\"initializing\">正在初始化</string>\n    <string name=\"verifying\">正在校驗</string>\n    <string name=\"sideload_geoip\">旁加載 GEOIP</string>\n    <string name=\"sideload_geoip_summary\">外部 GEOIP 數據庫</string>\n    <string name=\"force_enable\">強制啓用</string>\n    <string name=\"document\">文檔</string>\n    <string name=\"clash_wiki\">Clash Wiki</string>\n    <string name=\"invalid_file_name\">非法的文件名稱</string>\n    <string name=\"reset_override_settings\">重置覆寫設置</string>\n    <string name=\"reset_override_settings_message\">所有的覆寫設置將會被擦除</string>\n    <string name=\"key\">鍵</string>\n    <string name=\"more\">更多</string>\n    <string name=\"save\">保存</string>\n    <string name=\"delay_test\">延遲測試</string>\n    <string name=\"format_provider_type\">%1$s(%2$s)</string>\n    <string name=\"rule\">規則</string>\n    <string name=\"http\">HTTP</string>\n    <string name=\"compatible\">兼容</string>\n    <string name=\"format_update_provider_failure\">更新 %1$s: %2$s</string>\n    <string name=\"update_all\">更新全部</string>\n    <string name=\"proxy_empty_tips\">沒有可以顯示的組</string>\n    <string name=\"reverse\">反轉</string>\n    <string name=\"close\">關閉</string>\n    <string name=\"keyword\">關鍵詞</string>\n    <string name=\"invalid_log_file\">無效的日誌文件</string>\n    <string name=\"application_crashed\">應用崩潰</string>\n    <string name=\"application_broken_tips\">應用缺少必要的運行組件，這通常由於下載不完整的 apk 導致。</string>\n    <string name=\"reinstall\">重新安裝</string>\n    <string name=\"github_releases\">Github Releases</string>\n    <string name=\"unable_to_start_vpn\">無法啓動 VPN 組件</string>\n    <string name=\"request_donate_tips\">如果您覺得本應用對您有幫助歡迎在 [幫助] 中給予開發者一點捐贈</string>\n    <string name=\"request_donate\">捐贈</string>\n    <string name=\"version_updated\">應用已更新</string>\n    <string name=\"version_updated_tips\">設置已被清除，舊的配置文件需要再次保存。</string>\n    <string name=\"active_unsaved_tips\">配置文件需要在激活之前保存</string>\n    <string name=\"mode_switch_tips\">僅在本次會話中有效</string>\n    <string name=\"import_\">導入</string>\n    <string name=\"sources\">源代碼</string>\n    <string name=\"clash_core\">Clash 核心</string>\n    <string name=\"name_server_policy\">Name Server 策略</string>\n    <string name=\"block_loopback\">阻止本地迴環</string>\n    <string name=\"block_loopback_summary\">阻止本地迴環連接</string>\n    <string name=\"developer_now\">楓靈劍影（當前版本）</string>\n    <string name=\"developer_before\">kr238（原始版本）</string>\n    <string name=\"permission_request_title\">權限請求</string>\n    <string name=\"permission_notification_desc\">Clash You 需要通知權限以在通知欄顯示流量信息</string>\n    <string name=\"permission_request_positive\">前往授予</string>\n    <string name=\"permission_request_negative\">暫時不用</string>\n</resources>"
  },
  {
    "path": "design/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"access_control_packages_summary\">設定應用程式存取權</string>\n    <string name=\"about\">關於</string>\n    <string name=\"access_control_mode\">存取控制套件</string>\n    <string name=\"access_control_packages\">存取控制應用程式套件清單</string>\n    <string name=\"append_system_dns\">附加作業系統 DNS</string>\n    <string name=\"application_broken\">應用程式損毀</string>\n    <string name=\"application_name\">Clash You</string>\n    <string name=\"auto_update\">自動更新</string>\n    <string name=\"behavior\">行為</string>\n    <string name=\"bypass_private_network\">略過私有網路</string>\n    <string name=\"bypass_private_network_summary\">略過私有網路位址</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"clash_logcat\">Clash You Logcat</string>\n    <string name=\"create_profile\">建立設定檔</string>\n    <string name=\"dark_mode\">深色模式</string>\n    <string name=\"default_\">預設</string>\n    <string name=\"delay\">延遲</string>\n    <string name=\"delete\">刪除</string>\n    <string name=\"delete_all_logs\">刪除所有紀錄檔</string>\n    <string name=\"delete_all_logs_warn\">所有歷史紀錄將被刪除</string>\n    <string name=\"detail\">詳細資料</string>\n    <string name=\"direct_mode\">直連模式</string>\n    <string name=\"disabled\">已停用</string>\n    <string name=\"dns_hijacking\">DNS 攔截攻擊</string>\n    <string name=\"dns_hijacking_summary\">處理所有 DNS 封包</string>\n    <string name=\"duplicate\">複製</string>\n    <string name=\"edit\">編輯</string>\n    <string name=\"empty_name\">空白名稱</string>\n    <string name=\"exit_without_save\">退出而不儲存</string>\n    <string name=\"exit_without_save_warning\">將遺失所有變更</string>\n    <string name=\"export\">匯出</string>\n    <string name=\"external\">外部</string>\n    <string name=\"file\">檔案</string>\n    <string name=\"file_exported\">檔案已匯出</string>\n    <string name=\"format_minutes\">%d 分鐘</string>\n    <string name=\"format_profile_activated\">%s 已啟用</string>\n    <string name=\"format_traffic_forwarded\">%s 已轉送</string>\n    <string name=\"global_mode\">全域模式</string>\n    <string name=\"history\">歷史</string>\n    <string name=\"import_from_file\">從檔案匯入</string>\n    <string name=\"import_from_url\">從 URL 匯入</string>\n    <string name=\"interface_\">介面</string>\n    <string name=\"invalid_url\">無效 URL</string>\n    <string name=\"launch_name\">Clash You</string>\n    <string name=\"logcat\">Logcat</string>\n    <string name=\"logs\">紀錄</string>\n    <string name=\"mode\">模式</string>\n    <string name=\"name\">名稱</string>\n    <string name=\"network\">網路</string>\n    <string name=\"new_profile\">新設定檔</string>\n    <string name=\"not_selected\">未選擇</string>\n    <string name=\"ok\">確認</string>\n    <string name=\"profile\">設定檔</string>\n    <string name=\"profile_name\">設定檔名稱</string>\n    <string name=\"profiles\">設定檔</string>\n    <string name=\"properties\">性質</string>\n    <string name=\"proxy\">Proxy</string>\n    <string name=\"recently\">最近</string>\n    <string name=\"route_system_traffic\">自動轉送作業系統流量</string>\n    <string name=\"routing_via_vpn_service\">通過 VpnService 自動轉送所有作業系統流量</string>\n    <string name=\"rule_mode\">規則模式</string>\n    <string name=\"running\">執行中</string>\n    <string name=\"settings\">設定</string>\n    <string name=\"show_traffic\">顯示流量</string>\n    <string name=\"show_traffic_summary\">在通知中自動重新整理流量</string>\n    <string name=\"allow_clash_auto_restart\">允許 Clash You 自動重新啟動</string>\n    <string name=\"auto_restart\">自動重新啟動</string>\n    <string name=\"stopped\">已停止</string>\n    <string name=\"help\">幫助</string>\n    <string name=\"tap_to_start\">輕觸以啟動</string>\n    <string name=\"update\">更新</string>\n    <string name=\"url\">URL</string>\n    <string name=\"vpn_service_options\">VpnService 選項</string>\n    <string name=\"options_unavailable\">選項在 Clash You 執行時不可用</string>\n    <string name=\"search\">搜尋</string>\n    <string name=\"system_apps\">系統程式</string>\n    <string name=\"update_time\">更新時間</string>\n    <string name=\"package_name\">套件名稱</string>\n    <string name=\"install_time\">安裝時間</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"feedback\">回饋</string>\n    <string name=\"github_issues\">GitHub Issues</string>\n    <string name=\"tips_properties\"><![CDATA[僅接受 <strong>Clash 設定檔</strong> (包含<strong>Proxy</strong> /<strong>規則</strong>)]]></string>\n    <string name=\"loading\">載入中</string>\n    <string name=\"tips_help\"><![CDATA[Clash You 是一個<strong>免費應用程式</strong>並且我們<strong>不</strong>為其提供任何服務, <strong>請務必不要回報非應用程式自身引起的問題</strong>]]></string>\n    <string name=\"donate\">抖內</string>\n    <string name=\"allow_all_apps\">允許所有應用程式</string>\n    <string name=\"allow_selected_apps\">僅允許已選擇的應用程式</string>\n    <string name=\"deny_selected_apps\">不允許已選擇的應用程式</string>\n    <string name=\"no_profile_selected\">未選擇設定檔</string>\n    <string name=\"copied\">已複製</string>\n    <string name=\"script_mode\">腳本模式</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"filter\">過濾</string>\n    <string name=\"select_all\">全選</string>\n    <string name=\"select_invert\">反選</string>\n    <string name=\"select_none\">清除</string>\n    <string name=\"app\">應用程式</string>\n    <string name=\"follow_system_android_10\">跟隨系統設定 (Android 10+)</string>\n    <string name=\"always_dark\">深色</string>\n    <string name=\"always_light\">淺色</string>\n    <string name=\"service\">服務</string>\n    <string name=\"accept_http_content\">僅接受 http(s) 和 content 類型</string>\n    <string name=\"at_least_15_minutes\">至少 15 分鐘</string>\n    <string name=\"override\">覆寫</string>\n    <string name=\"general\">一般</string>\n    <string name=\"dns\">DNS</string>\n    <string name=\"http_port\">HTTP 埠</string>\n    <string name=\"socks_port\">Socks 埠</string>\n    <string name=\"mixed_port\">Mixed 埠</string>\n    <string name=\"allow_lan\">允許來自區域網路的連線</string>\n    <string name=\"bind_address\">監聽位址</string>\n    <string name=\"log_level\">紀錄等級</string>\n    <string name=\"ipv6\">IPv6</string>\n    <string name=\"hosts\">Hosts</string>\n    <string name=\"enabled\">已啟用</string>\n    <string name=\"info\">資訊</string>\n    <string name=\"warning\">警告</string>\n    <string name=\"error\">錯誤</string>\n    <string name=\"debug\">偵錯</string>\n    <string name=\"silent\">停用</string>\n    <string name=\"format_elements\">%d 個條目</string>\n    <string name=\"strategy\">策略</string>\n    <string name=\"enhanced_mode\">增強模式</string>\n    <string name=\"name_server\">名稱伺服器</string>\n    <string name=\"fallback\">備用名稱伺服器</string>\n    <string name=\"default_name_server\">預設名稱伺服器</string>\n    <string name=\"fakeip_filter\">FakeIP 過濾器</string>\n    <string name=\"geoip_fallback\">GeoIP 備用</string>\n    <string name=\"ipcidr_fallback\">IPCIDR 備用</string>\n    <string name=\"use_built_in\">使用內建</string>\n    <string name=\"mapping\">Real-IP 至域名映射</string>\n    <string name=\"fakeip\">Fake-IP 至域名映射</string>\n    <string name=\"sort\">排序</string>\n    <string name=\"layout\">版面配置</string>\n    <string name=\"single\">單欄</string>\n    <string name=\"multiple\">多欄</string>\n    <string name=\"not_selectable\">無法選擇</string>\n    <string name=\"providers\">提供者</string>\n    <string name=\"unavailable\">無法使用</string>\n    <string name=\"_new\">新增</string>\n    <string name=\"value\">值</string>\n    <string name=\"listen\">監聽</string>\n    <string name=\"files\">檔案</string>\n    <string name=\"browse_files\">瀏覽檔案</string>\n    <string name=\"browse_configuration_providers\">瀏覽設定檔和外部資源</string>\n    <string name=\"rename\">重新命名</string>\n    <string name=\"file_name\">檔名</string>\n    <string name=\"format_type_unsaved\">%s (未儲存)</string>\n    <string name=\"format_type_saved\">%1$s (%2$s)</string>\n    <string name=\"format_profile_usage\">%1$s / %2$s</string>\n    <string name=\"format_minutes_ago\">%d 分鐘前</string>\n    <string name=\"format_hours_ago\">%d 小時前</string>\n    <string name=\"format_days_ago\">%d 天前</string>\n    <string name=\"format_months_ago\">%d 月前</string>\n    <string name=\"format_years_ago\">%d 年前</string>\n    <string name=\"system_proxy\">作業系統 Proxy</string>\n    <string name=\"system_proxy_summary\">為 VpnService 附加 HTTP Proxy</string>\n    <string name=\"dont_modify\">不修改</string>\n    <string name=\"redirect_port\">Redirect 埠</string>\n    <string name=\"tproxy_port\">TProxy 埠</string>\n    <string name=\"reset\">重設</string>\n    <string name=\"use_hosts\">使用 Hosts</string>\n    <string name=\"authentication\">認證</string>\n    <string name=\"domain_fallback\">備用域名</string>\n    <string name=\"empty\">空</string>\n    <string name=\"import_from_clipboard\">從剪貼簿匯入</string>\n    <string name=\"export_to_clipboard\">匯出至剪貼簿</string>\n    <string name=\"auto_update_minutes\">自動更新 (分鐘)</string>\n    <string name=\"profile_url\">設定檔 URL</string>\n    <string name=\"should_not_be_blank\">不能為空</string>\n    <string name=\"format_fetching_configuration\">正在從 %s 下載設定檔</string>\n    <string name=\"format_fetching_provider\">正在下載額外資源 %s</string>\n    <string name=\"initializing\">正在初始化</string>\n    <string name=\"verifying\">正在校驗</string>\n    <string name=\"sideload_geoip\">側載 GeoIP</string>\n    <string name=\"sideload_geoip_summary\">外部 GeoIP 資料庫</string>\n    <string name=\"force_enable\">強制啟用</string>\n    <string name=\"document\">文件</string>\n    <string name=\"clash_wiki\">Clash Wiki</string>\n    <string name=\"invalid_file_name\">無效的檔案名稱</string>\n    <string name=\"reset_override_settings\">重設覆寫設定</string>\n    <string name=\"reset_override_settings_message\">所有的覆寫設定將會被移除</string>\n    <string name=\"key\">鍵</string>\n    <string name=\"more\">更多</string>\n    <string name=\"save\">儲存</string>\n    <string name=\"delay_test\">延遲測試</string>\n    <string name=\"format_provider_type\">%1$s(%2$s)</string>\n    <string name=\"rule\">規則</string>\n    <string name=\"http\">HTTP</string>\n    <string name=\"compatible\">相容</string>\n    <string name=\"format_update_provider_failure\">更新 %1$s: %2$s</string>\n    <string name=\"update_all\">更新全部</string>\n    <string name=\"proxy_empty_tips\">沒有可以顯示的分組</string>\n    <string name=\"reverse\">反轉</string>\n    <string name=\"close\">關閉</string>\n    <string name=\"keyword\">關鍵字</string>\n    <string name=\"invalid_log_file\">無效的紀錄檔</string>\n    <string name=\"application_crashed\">應用程式崩潰</string>\n    <string name=\"application_broken_tips\">應用程式缺少必要的執行元件，這通常是由於下載了不完整的 APK 而導致。</string>\n    <string name=\"reinstall\">重新安裝</string>\n    <string name=\"github_releases\">GitHub Releases</string>\n    <string name=\"unable_to_start_vpn\">無法啟動 VPN 元件</string>\n    <string name=\"request_donate_tips\">如果您覺得本應用程式對您有幫助，歡迎在 [幫助] 中給予開發人員一些抖內</string>\n    <string name=\"request_donate\">抖內</string>\n    <string name=\"version_updated\">應用程式已更新</string>\n    <string name=\"version_updated_tips\">設定已被清除，舊版設定檔需要再次儲存。</string>\n    <string name=\"active_unsaved_tips\">設定檔需要在開啟之前儲存</string>\n    <string name=\"mode_switch_tips\">僅在本次工作階段中有效</string>\n    <string name=\"import_\">匯入</string>\n    <string name=\"sources\">原始碼</string>\n    <string name=\"clash_core\">Clash 核心</string>\n    <string name=\"name_server_policy\">Name Server 政策</string>\n    <string name=\"block_loopback\">阻擋本地迴送</string>\n    <string name=\"block_loopback_summary\">阻擋本地回連結</string>\n    <string name=\"geoip_fallback_code\">GeoIP 備用區域代碼</string>\n    <string name=\"allow_bypass\">允許應用程式繞過</string>\n    <string name=\"allow_bypass_summary\">允許其他程式繞過 VPN</string>\n    <string name=\"sniff_tls_sni\">嗅探 TLS 的 SNI</string>\n    <string name=\"developer_now\">楓靈劍影（當前版本）</string>\n    <string name=\"developer_before\">kr238（原始版本）</string>\n    <string name=\"permission_request_title\">權限請求</string>\n    <string name=\"permission_notification_desc\">Clash You 需要通知權限以在通知欄顯示流量信息</string>\n    <string name=\"permission_request_positive\">前往授予</string>\n    <string name=\"permission_request_negative\">暫時不用</string>\n</resources>\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4.2-all.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionSha256Sum=e6d864e3b5bc05cc62041842b306383fc1fefcec359e70cebb1d470a6094ca82"
  },
  {
    "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=-Xmx4g -XX:+UseParallelGC -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# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Gradle parallel build\norg.gradle.parallel=true"
  },
  {
    "path": "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/master/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\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=${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} >&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        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        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# 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": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "hideapi/build.gradle.kts",
    "content": "plugins {\n    id(\"com.android.library\")\n}\n"
  },
  {
    "path": "hideapi/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "hideapi/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": "hideapi/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"yos.clash.material.hideapi\" />"
  },
  {
    "path": "hideapi/src/main/java/android/app/ActivityThread.java",
    "content": "package android.app;\n\npublic class ActivityThread {\n    public static String currentProcessName() {\n        throw new IllegalArgumentException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "service/build.gradle.kts",
    "content": "plugins {\n    kotlin(\"android\")\n    id(\"kotlinx-serialization\")\n    id(\"com.android.library\")\n    id(\"com.google.devtools.ksp\")\n}\n\ndependencies {\n    implementation(project(\":core\"))\n    implementation(project(\":common\"))\n\n    ksp(libs.kaidl.compiler)\n    ksp(libs.androidx.room.compiler)\n\n    implementation(libs.kotlin.coroutine)\n    implementation(libs.kotlin.serialization.json)\n    implementation(libs.androidx.core)\n    implementation(libs.androidx.room.runtime)\n    implementation(libs.androidx.room.ktx)\n    implementation(libs.kaidl.runtime)\n    implementation(libs.rikkax.multiprocess)\n}\n\nafterEvaluate {\n    android {\n        libraryVariants.forEach {\n            sourceSets[it.name].kotlin.srcDir(buildDir.resolve(\"generated/ksp/${it.name}/kotlin\"))\n            sourceSets[it.name].java.srcDir(buildDir.resolve(\"generated/ksp/${it.name}/java\"))\n        }\n    }\n}"
  },
  {
    "path": "service/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "service/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.\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"
  },
  {
    "path": "service/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"yos.clash.material.service\">\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" />\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n\n    <application>\n        <service\n            android:name=\".ClashService\"\n            android:exported=\"false\"\n            android:label=\"@string/clash_for_android\"\n            android:foregroundServiceType=\"specialUse\"\n            android:process=\":background\" />\n        <service\n            android:name=\".TunService\"\n            android:exported=\"false\"\n            android:label=\"@string/clash_for_android\"\n            android:foregroundServiceType=\"specialUse\"\n            android:permission=\"android.permission.BIND_VPN_SERVICE\"\n            android:process=\":background\">\n            <intent-filter>\n                <action android:name=\"android.net.VpnService\" />\n            </intent-filter>\n        </service>\n        <service\n            android:name=\".RemoteService\"\n            android:exported=\"false\"\n            android:foregroundServiceType=\"specialUse\"\n            android:process=\":background\" />\n        <service\n            android:name=\".ProfileWorker\"\n            android:exported=\"false\"\n            android:foregroundServiceType=\"specialUse\"\n            android:process=\":background\" />\n\n        <provider\n            android:name=\".FilesProvider\"\n            android:authorities=\"${applicationId}.files\"\n            android:exported=\"true\"\n            android:grantUriPermissions=\"true\"\n            android:permission=\"android.permission.MANAGE_DOCUMENTS\"\n            android:process=\":background\">\n            <intent-filter>\n                <action android:name=\"android.content.action.DOCUMENTS_PROVIDER\" />\n            </intent-filter>\n        </provider>\n        <provider\n            android:name=\".StatusProvider\"\n            android:authorities=\"${applicationId}.status\"\n            android:exported=\"false\"\n            android:process=\":background\" />\n        <provider\n            android:name=\".PreferenceProvider\"\n            android:authorities=\"${applicationId}.settings\"\n            android:exported=\"false\"\n            android:process=\":background\" />\n\n        <receiver\n            android:name=\".ProfileReceiver\"\n            android:enabled=\"true\"\n            android:exported=\"true\"\n            android:permission=\"${applicationId}.permission.RECEIVE_BROADCASTS\"\n            android:process=\":background\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.BOOT_COMPLETED\" />\n                <action android:name=\"android.intent.action.MY_PACKAGE_REPLACED\" />\n                <action android:name=\"android.intent.action.TIME_SET\" />\n                <action android:name=\"android.intent.action.TIMEZONE_CHANGED\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"{applicationId}.intent.action.PROFILE_REQUEST_UPDATE\" />\n                <data android:scheme=\"uuid\" />\n            </intent-filter>\n        </receiver>\n    </application>\n</manifest>\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/BaseService.kt",
    "content": "package yos.clash.material.service\n\nimport android.app.Service\nimport yos.clash.material.service.util.cancelAndJoinBlocking\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\n\nabstract class BaseService : Service(), CoroutineScope by CoroutineScope(Dispatchers.Default) {\n    override fun onDestroy() {\n        super.onDestroy()\n\n        cancelAndJoinBlocking()\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/ClashManager.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.Context\nimport com.github.kr328.clash.core.model.LogMessage\nimport com.github.kr328.clash.core.model.UiConfiguration\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport com.github.kr328.clash.core.model.ConfigurationOverride\nimport com.github.kr328.clash.core.model.Provider\nimport com.github.kr328.clash.core.model.ProviderList\nimport com.github.kr328.clash.core.model.ProxyGroup\nimport com.github.kr328.clash.core.model.ProxySort\nimport com.github.kr328.clash.core.model.TunnelState\nimport yos.clash.material.service.data.Selection\nimport yos.clash.material.service.data.SelectionDao\nimport yos.clash.material.service.remote.IClashManager\nimport yos.clash.material.service.remote.ILogObserver\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.sendOverrideChanged\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.channels.ReceiveChannel\n\nclass ClashManager(private val context: Context) : IClashManager,\n    CoroutineScope by CoroutineScope(Dispatchers.IO) {\n    private val store = ServiceStore(context)\n    private var logReceiver: ReceiveChannel<LogMessage>? = null\n\n    override fun queryTunnelState(): TunnelState {\n        return Clash.queryTunnelState()\n    }\n\n    override fun queryTrafficTotal(): Long {\n        return Clash.queryTrafficTotal()\n    }\n\n    override fun queryProxyGroupNames(excludeNotSelectable: Boolean): List<String> {\n        return Clash.queryGroupNames(excludeNotSelectable)\n    }\n\n    override fun queryProxyGroup(name: String, proxySort: ProxySort): ProxyGroup {\n        return Clash.queryGroup(name, proxySort)\n    }\n\n    override fun queryConfiguration(): UiConfiguration {\n        return Clash.queryConfiguration()\n    }\n\n    override fun queryProviders(): ProviderList {\n        return ProviderList(Clash.queryProviders())\n    }\n\n    override fun queryOverride(slot: Clash.OverrideSlot): ConfigurationOverride {\n        return Clash.queryOverride(slot)\n    }\n\n    override fun patchSelector(group: String, name: String): Boolean {\n        return Clash.patchSelector(group, name).also {\n            val current = store.activeProfile ?: return@also\n\n            if (it) {\n                SelectionDao().setSelected(Selection(current, group, name))\n            } else {\n                SelectionDao().removeSelected(current, group)\n            }\n        }\n    }\n\n    override fun patchOverride(slot: Clash.OverrideSlot, configuration: ConfigurationOverride) {\n        Clash.patchOverride(slot, configuration)\n\n        context.sendOverrideChanged()\n    }\n\n    override fun clearOverride(slot: Clash.OverrideSlot) {\n        Clash.clearOverride(slot)\n    }\n\n    override suspend fun healthCheck(group: String) {\n        return Clash.healthCheck(group).await()\n    }\n\n    override suspend fun updateProvider(type: Provider.Type, name: String) {\n        return Clash.updateProvider(type, name).await()\n    }\n\n    override fun setLogObserver(observer: ILogObserver?) {\n        synchronized(this) {\n            logReceiver?.apply {\n                cancel()\n\n                Clash.forceGc()\n            }\n\n            if (observer != null) {\n                logReceiver = Clash.subscribeLogcat().also { c ->\n                    launch {\n                        try {\n                            while (isActive) {\n                                observer.newItem(c.receive())\n                            }\n                        } catch (e: CancellationException) {\n                            // intended behavior\n                            // ignore\n                        } catch (e: Exception) {\n                            Log.w(\"UI crashed\", e)\n                        } finally {\n                            withContext(NonCancellable) {\n                                c.cancel()\n\n                                Clash.forceGc()\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/ClashService.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.Intent\nimport android.os.Binder\nimport android.os.IBinder\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.service.clash.clashRuntime\nimport yos.clash.material.service.clash.module.*\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.cancelAndJoinBlocking\nimport yos.clash.material.service.util.sendClashStarted\nimport yos.clash.material.service.util.sendClashStopped\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.selects.select\nimport kotlinx.coroutines.withContext\n\nclass ClashService : BaseService() {\n    private val self: ClashService\n        get() = this\n\n    private var reason: String? = null\n\n    private val runtime = clashRuntime {\n        val store = ServiceStore(self)\n\n        val close = install(CloseModule(self))\n        val config = install(ConfigurationModule(self))\n        val network = install(NetworkObserveModule(self))\n        val sideload = install(SideloadDatabaseModule(self))\n\n        if (store.dynamicNotification)\n            install(DynamicNotificationModule(self))\n        else\n            install(StaticNotificationModule(self))\n\n        install(AppListCacheModule(self))\n        install(TimeZoneModule(self))\n        install(SuspendModule(self))\n\n        try {\n            while (isActive) {\n                val quit = select<Boolean> {\n                    close.onEvent {\n                        true\n                    }\n                    config.onEvent {\n                        reason = it.message\n\n                        true\n                    }\n                    sideload.onEvent {\n                        reason = it.message\n\n                        true\n                    }\n                    network.onEvent {\n                        false\n                    }\n                }\n\n                if (quit) break\n            }\n        } catch (e: Exception) {\n            Log.e(\"Create clash runtime: ${e.message}\", e)\n\n            reason = e.message\n        } finally {\n            withContext(NonCancellable) {\n                stopSelf()\n            }\n        }\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n\n        if (StatusProvider.serviceRunning)\n            return stopSelf()\n\n        StatusProvider.serviceRunning = true\n\n        StaticNotificationModule.createNotificationChannel(this)\n        StaticNotificationModule.notifyLoadingNotification(this)\n\n        runtime.launch()\n    }\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {\n        sendClashStarted()\n\n        return START_STICKY\n    }\n\n    override fun onBind(intent: Intent?): IBinder {\n        return Binder()\n    }\n\n    override fun onDestroy() {\n        StatusProvider.serviceRunning = false\n\n        sendClashStopped(reason)\n\n        cancelAndJoinBlocking()\n\n        Log.i(\"ClashService destroyed: ${reason ?: \"successfully\"}\")\n\n        super.onDestroy()\n    }\n\n    override fun onTrimMemory(level: Int) {\n        super.onTrimMemory(level)\n\n        runtime.requestGc()\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/FilesProvider.kt",
    "content": "package yos.clash.material.service\n\nimport android.database.Cursor\nimport android.database.MatrixCursor\nimport android.os.Build\nimport android.os.CancellationSignal\nimport android.os.ParcelFileDescriptor\nimport android.provider.DocumentsContract.Root\nimport android.provider.DocumentsProvider\nimport yos.clash.material.common.util.PatternFileName\nimport yos.clash.material.service.document.*\nimport kotlinx.coroutines.runBlocking\nimport java.io.FileNotFoundException\nimport android.provider.DocumentsContract.Document as D\n\nclass FilesProvider : DocumentsProvider() {\n    companion object {\n        private const val DEFAULT_ROOT_ID = \"0\"\n\n        private val DEFAULT_DOCUMENT_COLUMNS = arrayOf(\n            D.COLUMN_DOCUMENT_ID,\n            D.COLUMN_DISPLAY_NAME,\n            D.COLUMN_MIME_TYPE,\n            D.COLUMN_LAST_MODIFIED,\n            D.COLUMN_SIZE,\n            D.COLUMN_FLAGS\n        )\n        private val DEFAULT_ROOT_COLUMNS = arrayOf(\n            Root.COLUMN_ROOT_ID,\n            Root.COLUMN_FLAGS,\n            Root.COLUMN_ICON,\n            Root.COLUMN_TITLE,\n            Root.COLUMN_SUMMARY,\n            Root.COLUMN_DOCUMENT_ID\n        )\n\n        private val FLAG_VIRTUAL: Int =\n            if (Build.VERSION.SDK_INT >= 24) D.FLAG_VIRTUAL_DOCUMENT else 0\n    }\n\n    private val picker: Picker by lazy {\n        Picker(context!!)\n    }\n\n    override fun openDocument(\n        documentId: String?,\n        mode: String?,\n        signal: CancellationSignal?\n    ): ParcelFileDescriptor {\n        val m = ParcelFileDescriptor.parseMode(mode)\n\n        return runBlocking {\n            val path = Paths.resolve(documentId ?: \"/\")\n\n            val document = picker.pick(path, mode?.requestWrite ?: true)\n\n            require(document is FileDocument) {\n                throw FileNotFoundException(\"invalid path $documentId\")\n            }\n\n            ParcelFileDescriptor.open(document.file, m)\n        }\n    }\n\n    override fun deleteDocument(documentId: String?) {\n        val documentPath = documentId ?: \"/\"\n\n        runBlocking {\n            val path = Paths.resolve(documentPath)\n\n            if (path.relative == null)\n                throw IllegalArgumentException(\"invalid path $documentId\")\n\n            val document = picker.pick(path, true)\n\n            require(document is FileDocument) {\n                throw FileNotFoundException(\"invalid path $documentId\")\n            }\n\n            document.file.deleteRecursively()\n        }\n    }\n\n    override fun renameDocument(documentId: String?, displayName: String?): String {\n        val name = displayName ?: \"\"\n\n        if (!PatternFileName.matches(name))\n            throw IllegalArgumentException(\"invalid name $displayName\")\n\n        return runBlocking {\n            val path = Paths.resolve(documentId ?: \"/\")\n\n            if (path.relative == null)\n                throw IllegalArgumentException(\"unable to rename $documentId\")\n\n            val document = picker.pick(path, true)\n\n            require(document is FileDocument) {\n                throw IllegalArgumentException(\"unable to rename $document\")\n            }\n\n            val parent = document.file.parentFile\n\n            require(parent != null) {\n                throw IllegalArgumentException(\"unable to rename $document\")\n            }\n\n            document.file.renameTo(parent.resolve(name))\n\n            path.copy(relative = path.relative.dropLast(1) + name).toString()\n        }\n    }\n\n    override fun queryChildDocuments(\n        parentDocumentId: String?,\n        projection: Array<out String>?,\n        sortOrder: String?\n    ): Cursor {\n        return runBlocking {\n            try {\n                val doc = parentDocumentId ?: \"/\"\n                val path = Paths.resolve(doc)\n                val documents = picker.list(path)\n\n                MatrixCursor(resolveDocumentProjection(projection)).apply {\n                    documents.forEach {\n                        newRow().applyDocument(it)\n                            .add(D.COLUMN_DOCUMENT_ID, \"$doc/${it.id}\")\n                    }\n                }\n            } catch (e: Exception) {\n                MatrixCursor(resolveDocumentProjection(projection))\n            }\n        }\n    }\n\n    override fun queryDocument(documentId: String?, projection: Array<out String>?): Cursor {\n        return runBlocking {\n            try {\n                val doc = documentId ?: \"/\"\n                val path = Paths.resolve(doc)\n                val document = picker.pick(path, false)\n\n                MatrixCursor(resolveDocumentProjection(projection)).apply {\n                    newRow().applyDocument(document).add(D.COLUMN_DOCUMENT_ID, doc)\n                }\n            } catch (e: Exception) {\n                MatrixCursor(resolveDocumentProjection(projection))\n            }\n        }\n    }\n\n    override fun onCreate(): Boolean {\n        return true\n    }\n\n    override fun queryRoots(projection: Array<out String>?): Cursor {\n        val flags = Root.FLAG_LOCAL_ONLY or Root.FLAG_SUPPORTS_IS_CHILD\n\n        return MatrixCursor(projection ?: DEFAULT_ROOT_COLUMNS).apply {\n            newRow().apply {\n                add(Root.COLUMN_ROOT_ID, DEFAULT_ROOT_ID)\n                add(Root.COLUMN_FLAGS, flags)\n                add(Root.COLUMN_ICON, R.drawable.ic_logo_service)\n                add(Root.COLUMN_TITLE, context!!.getString(R.string.clash_for_android))\n                add(Root.COLUMN_SUMMARY, context!!.getString(R.string.profiles_and_providers))\n                add(Root.COLUMN_DOCUMENT_ID, \"/\")\n                add(Root.COLUMN_MIME_TYPES, D.MIME_TYPE_DIR)\n            }\n        }\n    }\n\n    override fun isChildDocument(parentDocumentId: String?, documentId: String?): Boolean {\n        if (parentDocumentId == null || documentId == null)\n            return false\n\n        return documentId.startsWith(parentDocumentId)\n    }\n\n    private fun MatrixCursor.RowBuilder.applyDocument(document: Document): MatrixCursor.RowBuilder {\n        var flags = 0\n\n        document.flags.forEach {\n            flags = when (it) {\n                Flag.Writable -> flags or D.FLAG_SUPPORTS_WRITE\n                Flag.Deletable -> flags or D.FLAG_SUPPORTS_DELETE\n                Flag.Virtual -> flags or FLAG_VIRTUAL\n            }\n        }\n\n        add(D.COLUMN_DISPLAY_NAME, document.name)\n        add(D.COLUMN_MIME_TYPE, document.mimeType)\n        add(D.COLUMN_LAST_MODIFIED, document.updatedAt)\n        add(D.COLUMN_SIZE, document.size)\n        add(D.COLUMN_FLAGS, flags)\n\n        return this\n    }\n\n    private fun resolveDocumentProjection(projection: Array<out String>?): Array<out String> {\n        return projection ?: DEFAULT_DOCUMENT_COLUMNS\n    }\n\n    private val String.requestWrite: Boolean\n        get() {\n            return contains(\"w\", ignoreCase = true)\n        }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/PreferenceProvider.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.Context\nimport android.content.SharedPreferences\nimport yos.clash.material.common.constants.Authorities\nimport rikka.preference.MultiProcessPreference\nimport rikka.preference.PreferenceProvider\n\nclass PreferenceProvider : PreferenceProvider() {\n    override fun onCreatePreference(context: Context): SharedPreferences {\n        return context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)\n    }\n\n    companion object {\n        private const val FILE_NAME = \"service\"\n\n        fun createSharedPreferencesFromContext(context: Context): SharedPreferences {\n            return when (context) {\n                is BaseService, is TunService ->\n                    context.getSharedPreferences(\n                        FILE_NAME,\n                        Context.MODE_PRIVATE\n                    )\n                else ->\n                    MultiProcessPreference(\n                        context,\n                        Authorities.SETTINGS_PROVIDER\n                    )\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/ProfileManager.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.Context\nimport yos.clash.material.service.data.Database\nimport yos.clash.material.service.data.ImportedDao\nimport yos.clash.material.service.data.Pending\nimport yos.clash.material.service.data.PendingDao\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.service.remote.IFetchObserver\nimport yos.clash.material.service.remote.IProfileManager\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.directoryLastModified\nimport yos.clash.material.service.util.generateProfileUUID\nimport yos.clash.material.service.util.importedDir\nimport yos.clash.material.service.util.pendingDir\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport yos.clash.material.service.data.ProviderMoreInfoDao\nimport yos.clash.material.service.model.MoreInfo\nimport java.io.FileNotFoundException\nimport java.util.*\n\nclass ProfileManager(private val context: Context) : IProfileManager,\n    CoroutineScope by CoroutineScope(Dispatchers.IO) {\n    private val store = ServiceStore(context)\n\n    init {\n        launch {\n            Database.database //.init\n\n            ProfileReceiver.rescheduleAll(context)\n        }\n    }\n\n    override suspend fun create(type: Profile.Type, name: String, source: String): UUID {\n        val uuid = generateProfileUUID()\n        val pending = Pending(\n            uuid = uuid,\n            name = name,\n            type = type,\n            source = source,\n            interval = 0,\n        )\n\n        PendingDao().insert(pending)\n\n        context.pendingDir.resolve(uuid.toString()).apply {\n            deleteRecursively()\n            mkdirs()\n\n            @Suppress(\"BlockingMethodInNonBlockingContext\")\n            resolve(\"config.yaml\").createNewFile()\n            resolve(\"providers\").mkdir()\n        }\n\n        return uuid\n    }\n\n    override suspend fun clone(uuid: UUID): UUID {\n        val newUUID = generateProfileUUID()\n\n        val imported = ImportedDao().queryByUUID(uuid)\n            ?: throw FileNotFoundException(\"profile $uuid not found\")\n\n        val pending = Pending(\n            uuid = newUUID,\n            name = imported.name,\n            type = Profile.Type.File,\n            source = imported.source,\n            interval = imported.interval,\n        )\n\n        cloneImportedFiles(uuid, newUUID)\n\n        PendingDao().insert(pending)\n\n        return newUUID\n    }\n\n    override suspend fun patch(uuid: UUID, name: String, source: String, interval: Long) {\n        val pending = PendingDao().queryByUUID(uuid)\n\n        if (pending == null) {\n            val imported = ImportedDao().queryByUUID(uuid)\n                ?: throw FileNotFoundException(\"profile $uuid not found\")\n\n            cloneImportedFiles(uuid)\n\n            PendingDao().insert(\n                Pending(\n                    uuid = imported.uuid,\n                    name = name,\n                    type = imported.type,\n                    source = source,\n                    interval = interval,\n                )\n            )\n        } else {\n            val newPending = pending.copy(\n                name = name,\n                source = source,\n                interval = interval\n            )\n\n            PendingDao().update(newPending)\n        }\n    }\n\n    override suspend fun update(uuid: UUID) {\n        scheduleUpdate(uuid, true)\n    }\n\n    override suspend fun commit(uuid: UUID, callback: IFetchObserver?) {\n        ProfileProcessor.apply(context, uuid, callback)\n\n        scheduleUpdate(uuid, false)\n    }\n\n    override suspend fun release(uuid: UUID) {\n        ProfileProcessor.release(context, uuid)\n    }\n\n    override suspend fun delete(uuid: UUID) {\n        ImportedDao().queryByUUID(uuid)?.also {\n            ProfileReceiver.cancelNext(context, it)\n        }\n\n        ProfileProcessor.delete(context, uuid)\n    }\n\n    override suspend fun queryByUUID(uuid: UUID): Profile? {\n        return resolveProfile(uuid)\n    }\n\n    override suspend fun queryAll(): List<Profile> {\n        val uuids = withContext(Dispatchers.IO) {\n            (ImportedDao().queryAllUUIDs() + PendingDao().queryAllUUIDs()).distinct()\n        }\n\n        return uuids.mapNotNull { resolveProfile(it) }\n    }\n\n    override suspend fun queryActive(): Profile? {\n        val active = store.activeProfile ?: return null\n\n        return if (ImportedDao().exists(active)) {\n            resolveProfile(active)\n        } else {\n            null\n        }\n    }\n\n    override suspend fun setActive(profile: Profile) {\n        ProfileProcessor.active(context, profile.uuid)\n    }\n\n    private suspend fun resolveProfile(uuid: UUID): Profile? {\n        val imported = ImportedDao().queryByUUID(uuid)\n        val pending = PendingDao().queryByUUID(uuid)\n        val subUserInfo = ProviderMoreInfoDao().queryByUUID(uuid)\n\n        val active = store.activeProfile\n        val name = pending?.name ?: imported?.name ?: return null\n        val type = pending?.type ?: imported?.type ?: return null\n        val source = pending?.source ?: imported?.source ?: return null\n        val interval = pending?.interval ?: imported?.interval ?: return null\n        val moreInfo = subUserInfo?.let {\n            MoreInfo(\n                uploadBase = it.upload,\n                downloadBase = it.download,\n                totalBase = it.total,\n                expireTime = it.expire,\n            )\n        }\n\n        return Profile(\n            uuid,\n            name,\n            type,\n            source,\n            active != null && imported?.uuid == active,\n            interval,\n            resolveUpdatedAt(uuid),\n            imported != null,\n            pending != null,\n            moreInfo\n        )\n    }\n\n    private fun resolveUpdatedAt(uuid: UUID): Long {\n        return context.pendingDir.resolve(uuid.toString()).directoryLastModified\n            ?: context.importedDir.resolve(uuid.toString()).directoryLastModified\n            ?: -1\n    }\n\n    private fun cloneImportedFiles(source: UUID, target: UUID = source) {\n        val s = context.importedDir.resolve(source.toString())\n        val t = context.pendingDir.resolve(target.toString())\n\n        if (!s.exists())\n            throw FileNotFoundException(\"profile $source not found\")\n\n        t.deleteRecursively()\n\n        s.copyRecursively(t)\n    }\n\n    private suspend fun scheduleUpdate(uuid: UUID, startImmediately: Boolean) {\n        val imported = ImportedDao().queryByUUID(uuid) ?: return\n\n        if (startImmediately) {\n            ProfileReceiver.schedule(context, imported)\n        } else {\n            ProfileReceiver.scheduleNext(context, imported)\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/ProfileProcessor.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.Context\nimport android.net.Uri\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport kotlinx.coroutines.Dispatchers\nimport yos.clash.material.service.data.Imported\nimport yos.clash.material.service.data.ImportedDao\nimport yos.clash.material.service.data.Pending\nimport yos.clash.material.service.data.PendingDao\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.service.remote.IFetchObserver\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.importedDir\nimport yos.clash.material.service.util.pendingDir\nimport yos.clash.material.service.util.processingDir\nimport yos.clash.material.service.util.sendProfileChanged\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlinx.coroutines.withContext\nimport yos.clash.material.service.data.ProviderMoreInfo\nimport yos.clash.material.service.data.ProviderMoreInfoDao\nimport yos.clash.material.service.model.MoreInfo\nimport java.net.HttpURLConnection\nimport java.net.URL\nimport java.util.*\nimport java.util.concurrent.TimeUnit\n\nobject ProfileProcessor {\n    private val profileLock = Mutex()\n    private val processLock = Mutex()\n\n    suspend fun apply(context: Context, uuid: UUID, callback: IFetchObserver? = null) {\n        withContext(NonCancellable) {\n            processLock.withLock {\n                val snapshot = profileLock.withLock {\n                    val pending = PendingDao().queryByUUID(uuid)\n                        ?: throw IllegalArgumentException(\"profile $uuid not found\")\n\n                    pending.enforceFieldValid()\n\n                    context.processingDir.deleteRecursively()\n                    context.processingDir.mkdirs()\n\n                    context.pendingDir.resolve(pending.uuid.toString())\n                        .copyRecursively(context.processingDir, overwrite = true)\n\n                    pending\n                }\n\n                val force = snapshot.type != Profile.Type.File\n                var cb = callback\n\n                Clash.fetchAndValid(context.processingDir, snapshot.source, force) {\n                    try {\n                        cb?.updateStatus(it)\n                    } catch (e: Exception) {\n                        cb = null\n\n                        Log.w(\"Report fetch status: $e\", e)\n                    }\n                }.await()\n\n                val subInfo = if (snapshot.type == Profile.Type.Url) {\n                    fetchSubscriptionUserInfo(snapshot.source)\n                } else {\n                    null\n                }\n\n                profileLock.withLock {\n                    if (PendingDao().queryByUUID(snapshot.uuid) == snapshot) {\n                        context.importedDir.resolve(snapshot.uuid.toString())\n                            .deleteRecursively()\n                        context.processingDir\n                            .copyRecursively(context.importedDir.resolve(snapshot.uuid.toString()))\n\n                        val old = ImportedDao().queryByUUID(snapshot.uuid)\n\n                        val new = Imported(\n                            snapshot.uuid,\n                            snapshot.name,\n                            snapshot.type,\n                            snapshot.source,\n                            snapshot.interval,\n                            old?.createdAt ?: System.currentTimeMillis()\n                        )\n\n                        if (old != null) {\n                            ImportedDao().update(new)\n                        } else {\n                            ImportedDao().insert(new)\n                        }\n\n                        PendingDao().remove(snapshot.uuid)\n\n                        subInfo?.let {\n                            ProviderMoreInfoDao().setInfo(\n                                ProviderMoreInfo(\n                                    uuid = snapshot.uuid,\n                                    upload = it.uploadBase,\n                                    download = it.downloadBase,\n                                    total = it.totalBase,\n                                    expire = it.expireTime\n                                )\n                            )\n                        }\n\n                        context.pendingDir.resolve(snapshot.uuid.toString())\n                            .deleteRecursively()\n\n                        context.sendProfileChanged(snapshot.uuid)\n                    }\n                }\n            }\n        }\n    }\n\n    suspend fun update(context: Context, uuid: UUID, callback: IFetchObserver?) {\n        withContext(NonCancellable) {\n            processLock.withLock {\n                val snapshot = profileLock.withLock {\n                    val imported = ImportedDao().queryByUUID(uuid)\n                        ?: throw IllegalArgumentException(\"profile $uuid not found\")\n\n                    context.processingDir.deleteRecursively()\n                    context.processingDir.mkdirs()\n\n                    context.importedDir.resolve(imported.uuid.toString())\n                        .copyRecursively(context.processingDir, overwrite = true)\n\n                    imported\n                }\n\n                var cb = callback\n\n                Clash.fetchAndValid(context.processingDir, snapshot.source, true) {\n                    try {\n                        cb?.updateStatus(it)\n                    } catch (e: Exception) {\n                        cb = null\n\n                        Log.w(\"Report fetch status: $e\", e)\n                    }\n                }.await()\n\n                val subInfo = if (snapshot.type == Profile.Type.Url) {\n                    fetchSubscriptionUserInfo(snapshot.source)\n                } else {\n                    null\n                }\n\n                profileLock.withLock {\n                    if (ImportedDao().exists(snapshot.uuid)) {\n                        context.importedDir.resolve(snapshot.uuid.toString()).deleteRecursively()\n                        context.processingDir\n                            .copyRecursively(context.importedDir.resolve(snapshot.uuid.toString()))\n\n                        subInfo?.let {\n                            ProviderMoreInfoDao().setInfo(\n                                ProviderMoreInfo(\n                                    uuid = snapshot.uuid,\n                                    upload = it.uploadBase,\n                                    download = it.downloadBase,\n                                    total = it.totalBase,\n                                    expire = it.expireTime\n                                )\n                            )\n                        }\n\n                        context.sendProfileChanged(snapshot.uuid)\n                    }\n                }\n            }\n        }\n    }\n\n    suspend fun delete(context: Context, uuid: UUID) {\n        withContext(NonCancellable) {\n            profileLock.withLock {\n                ImportedDao().remove(uuid)\n                PendingDao().remove(uuid)\n\n                val pending = context.pendingDir.resolve(uuid.toString())\n                val imported = context.importedDir.resolve(uuid.toString())\n\n                pending.deleteRecursively()\n                imported.deleteRecursively()\n\n                context.sendProfileChanged(uuid)\n            }\n        }\n    }\n\n    suspend fun release(context: Context, uuid: UUID): Boolean {\n        return withContext(NonCancellable) {\n            profileLock.withLock {\n                PendingDao().remove(uuid)\n\n                context.pendingDir.resolve(uuid.toString()).deleteRecursively()\n            }\n        }\n    }\n\n    suspend fun active(context: Context, uuid: UUID) {\n        withContext(NonCancellable) {\n            profileLock.withLock {\n                if (ImportedDao().exists(uuid)) {\n                    val store = ServiceStore(context)\n\n                    store.activeProfile = uuid\n\n                    context.sendProfileChanged(uuid)\n                }\n            }\n        }\n    }\n\n    private fun Pending.enforceFieldValid() {\n        val scheme = Uri.parse(source)?.scheme?.lowercase(Locale.getDefault())\n\n        when {\n            name.isBlank() ->\n                throw IllegalArgumentException(\"Empty name\")\n\n            source.isEmpty() && type != Profile.Type.File ->\n                throw IllegalArgumentException(\"Invalid url\")\n\n            source.isNotEmpty() && scheme != \"https\" && scheme != \"http\" && scheme != \"content\" ->\n                throw IllegalArgumentException(\"Unsupported url $source\")\n\n            interval != 0L && TimeUnit.MILLISECONDS.toMinutes(interval) < 15 ->\n                throw IllegalArgumentException(\"Invalid interval\")\n        }\n    }\n\n    private fun String.find(regex: String, group: Int): String? {\n        return regex.toRegex().find(this)?.groupValues?.get(group)\n    }\n\n    private suspend fun fetchSubscriptionUserInfo(url: String): MoreInfo? {\n        return try {\n            val userInfoStr = withContext(Dispatchers.IO) {\n                arrayOf(\"Clash\", \"Quantumultx\").forEach { ua ->\n                    val conn = URL(url).openConnection() as HttpURLConnection\n                    try {\n                        conn.requestMethod = \"GET\"\n                        conn.setRequestProperty(\"User-Agent\", ua)\n\n                        if (conn.responseCode == HttpURLConnection.HTTP_OK) {\n                            return@withContext conn.headerFields.entries.firstOrNull {\n                                \"subscription-userinfo\".equals(it.key, true)\n                            }?.value?.firstOrNull()\n                        }\n                    } finally {\n                        conn.disconnect()\n                    }\n                }\n                null\n            }\n\n            userInfoStr?.let {\n                MoreInfo(\n                    uploadBase = it.find(\"upload=(\\\\d+)\", 1)!!.toLong(),\n                    downloadBase = it.find(\"download=(\\\\d+)\", 1)!!.toLong(),\n                    totalBase = it.find(\"total=(\\\\d+)\", 1)!!.toLong(),\n                    expireTime = it.find(\"expire=(\\\\d+)\", 1)?.toLong()?.times(1000) ?: -1,\n                )\n            }\n        } catch (e: Exception) {\n            null\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/ProfileReceiver.kt",
    "content": "package yos.clash.material.service\n\nimport android.app.AlarmManager\nimport android.app.PendingIntent\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport androidx.core.content.getSystemService\nimport yos.clash.material.common.Global\nimport yos.clash.material.common.compat.pendingIntentFlags\nimport yos.clash.material.common.compat.startForegroundServiceCompat\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.common.util.componentName\nimport yos.clash.material.common.util.setUUID\nimport yos.clash.material.service.data.Imported\nimport yos.clash.material.service.data.ImportedDao\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.service.util.importedDir\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport java.util.concurrent.TimeUnit\n\nclass ProfileReceiver : BroadcastReceiver() {\n    override fun onReceive(context: Context, intent: Intent) {\n        when (intent.action) {\n            Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED,\n            Intent.ACTION_TIMEZONE_CHANGED, Intent.ACTION_TIME_CHANGED -> {\n                Global.launch {\n                    reset()\n\n                    val service = Intent(Intents.ACTION_PROFILE_SCHEDULE_UPDATES)\n                        .setComponent(ProfileWorker::class.componentName)\n\n                    context.startForegroundServiceCompat(service)\n                }\n            }\n            Intents.ACTION_PROFILE_REQUEST_UPDATE -> {\n                val redirect = intent.setComponent(ProfileWorker::class.componentName)\n\n                context.startForegroundServiceCompat(redirect)\n            }\n        }\n    }\n\n    companion object {\n        private val lock = Mutex()\n        private var initialized: Boolean = false\n\n        suspend fun rescheduleAll(context: Context) = lock.withLock {\n            if (initialized)\n                return\n\n            initialized = true\n\n            Log.i(\"Reschedule all profiles update\")\n\n            ImportedDao().queryAllUUIDs()\n                .mapNotNull { ImportedDao().queryByUUID(it) }\n                .filter { it.type != Profile.Type.File }\n                .forEach { scheduleNext(context, it) }\n        }\n\n        fun cancelNext(context: Context, imported: Imported) {\n            val intent = pendingIntentOf(context, imported)\n\n            context.getSystemService<AlarmManager>()?.cancel(intent)\n        }\n\n        fun schedule(context: Context, imported: Imported) {\n            val intent = pendingIntentOf(context, imported)\n\n            context.getSystemService<AlarmManager>()?.cancel(intent)\n\n            intent.send(context, 0, null)\n        }\n\n        fun scheduleNext(context: Context, imported: Imported) {\n            val intent = pendingIntentOf(context, imported)\n\n            context.getSystemService<AlarmManager>()?.cancel(intent)\n\n            if (imported.interval < TimeUnit.MINUTES.toMillis(15))\n                return\n\n            val current = System.currentTimeMillis()\n            val last = context.importedDir\n                .resolve(imported.uuid.toString())\n                .resolve(\"config.yaml\")\n                .lastModified()\n\n            // file not existed\n            if (last < 0)\n                return\n\n            val interval = (imported.interval - (current - last)).coerceAtLeast(0)\n\n            context.getSystemService<AlarmManager>()\n                ?.set(AlarmManager.RTC, current + interval, intent)\n        }\n\n        private suspend fun reset() = lock.withLock {\n            initialized = false\n        }\n\n        private fun pendingIntentOf(context: Context, imported: Imported): PendingIntent {\n            val intent = Intent(Intents.ACTION_PROFILE_REQUEST_UPDATE)\n                .setComponent(ProfileReceiver::class.componentName)\n                .setUUID(imported.uuid)\n\n            return PendingIntent.getBroadcast(\n                context,\n                0,\n                intent,\n                pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT)\n            )\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/ProfileWorker.kt",
    "content": "package yos.clash.material.service\n\nimport android.app.PendingIntent\nimport android.content.Intent\nimport android.content.pm.ServiceInfo\nimport android.os.Binder\nimport android.os.Build\nimport android.os.IBinder\nimport androidx.core.app.NotificationChannelCompat\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport yos.clash.material.common.compat.getColorCompat\nimport yos.clash.material.common.compat.pendingIntentFlags\nimport yos.clash.material.common.constants.Components\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.id.UndefinedIds\nimport yos.clash.material.common.util.setUUID\nimport yos.clash.material.common.util.uuid\nimport yos.clash.material.service.data.ImportedDao\nimport kotlinx.coroutines.*\nimport java.util.*\nimport java.util.concurrent.TimeUnit\n\nclass ProfileWorker : BaseService() {\n    private val service: ProfileWorker\n        get() = this\n\n    private val jobs = mutableListOf<Job>()\n\n    override fun onCreate() {\n        super.onCreate()\n\n        createChannels()\n\n        foreground()\n\n        launch {\n            delay(TimeUnit.SECONDS.toMillis(10))\n\n            while (true) {\n                jobs.removeFirstOrNull()?.join() ?: break\n            }\n\n            stopSelf()\n        }\n    }\n\n    override fun onDestroy() {\n        stopForeground(true)\n\n        super.onDestroy()\n    }\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {\n        super.onStartCommand(intent, flags, startId)\n\n        when (intent?.action) {\n            Intents.ACTION_PROFILE_REQUEST_UPDATE -> {\n                intent.uuid?.also {\n                    val job = launch {\n                        run(it)\n                    }\n\n                    jobs.add(job)\n                }\n            }\n            Intents.ACTION_PROFILE_SCHEDULE_UPDATES -> {\n                val job = launch {\n                    ProfileReceiver.rescheduleAll(service)\n\n                    delay(TimeUnit.SECONDS.toMillis(30))\n                }\n\n                jobs.add(job)\n            }\n        }\n\n        return START_NOT_STICKY\n    }\n\n    private suspend fun run(uuid: UUID) {\n        val imported = ImportedDao().queryByUUID(uuid) ?: return\n\n        try {\n            processing(imported.name) {\n                ProfileProcessor.update(this, imported.uuid, null)\n            }\n\n            completed(imported.uuid, imported.name)\n\n            ProfileReceiver.scheduleNext(this, imported)\n        } catch (e: Exception) {\n            failed(imported.uuid, imported.name, e.message ?: \"Unknown\")\n        }\n    }\n\n    private fun createChannels() {\n        NotificationManagerCompat.from(this).createNotificationChannelsCompat(\n            listOf(\n                NotificationChannelCompat.Builder(\n                    SERVICE_CHANNEL,\n                    NotificationManagerCompat.IMPORTANCE_LOW\n                ).setName(getString(R.string.profile_service_status)).build(),\n                NotificationChannelCompat.Builder(\n                    STATUS_CHANNEL,\n                    NotificationManagerCompat.IMPORTANCE_LOW\n                ).setName(getString(R.string.profile_process_status)).build(),\n                NotificationChannelCompat.Builder(\n                    RESULT_CHANNEL,\n                    NotificationManagerCompat.IMPORTANCE_DEFAULT\n                ).setName(getString(R.string.profile_process_result)).build()\n            )\n        )\n    }\n\n    private fun foreground() {\n        val notification = NotificationCompat.Builder(this, SERVICE_CHANNEL)\n            .setContentTitle(getString(R.string.profile_updater))\n            .setContentText(getString(R.string.running))\n            .setColor(getColorCompat(R.color.color_clash))\n            .setSmallIcon(R.drawable.ic_logo_service)\n            .setOngoing(true)\n            .setOnlyAlertOnce(true)\n            .build()\n\n        if (Build.VERSION.SDK_INT >= 34) {\n            startForeground(\n                R.id.nf_profile_worker, notification,\n                ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE\n            )\n        } else {\n            startForeground(R.id.nf_profile_worker, notification)\n        }\n    }\n\n    private suspend inline fun processing(name: String, block: () -> Unit) {\n        val id = UndefinedIds.next()\n\n        val notification = NotificationCompat.Builder(this, STATUS_CHANNEL)\n            .setContentTitle(getString(R.string.profile_updating))\n            .setContentText(name)\n            .setColor(getColorCompat(R.color.color_clash))\n            .setSmallIcon(R.drawable.ic_logo_service)\n            .setOngoing(true)\n            .setOnlyAlertOnce(true)\n            .setGroup(STATUS_CHANNEL)\n            .build()\n\n        NotificationManagerCompat.from(applicationContext)\n            .notify(id, notification)\n        try {\n            block()\n        } finally {\n            withContext(NonCancellable) {\n                NotificationManagerCompat.from(applicationContext)\n                    .cancel(id)\n            }\n        }\n    }\n\n    private fun resultBuilder(id: Int, uuid: UUID): NotificationCompat.Builder {\n        val intent = PendingIntent.getActivity(\n            this,\n            id,\n            Intent().setComponent(Components.PROPERTIES_ACTIVITY).setUUID(uuid),\n            pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT)\n        )\n\n        return NotificationCompat.Builder(this, RESULT_CHANNEL)\n            .setColor(getColorCompat(R.color.color_clash))\n            .setSmallIcon(R.drawable.ic_logo_service)\n            .setOnlyAlertOnce(true)\n            .setContentIntent(intent)\n            .setAutoCancel(true)\n            .setGroup(RESULT_CHANNEL)\n    }\n\n    private fun completed(uuid: UUID, name: String) {\n        val id = UndefinedIds.next()\n\n        val notification = resultBuilder(id, uuid)\n            .setContentTitle(getString(R.string.update_successfully))\n            .setContentText(getString(R.string.format_update_complete, name))\n            .build()\n\n        NotificationManagerCompat.from(this)\n            .notify(id, notification)\n    }\n\n    private fun failed(uuid: UUID, name: String, reason: String) {\n        val id = UndefinedIds.next()\n\n        val content = getString(R.string.format_update_failure, name, reason)\n\n        val notification = resultBuilder(id, uuid)\n            .setContentTitle(getString(R.string.update_failure))\n            .setContentText(content)\n            .setStyle(NotificationCompat.BigTextStyle().bigText(content))\n            .build()\n\n        NotificationManagerCompat.from(this)\n            .notify(id, notification)\n    }\n\n    companion object {\n        private const val SERVICE_CHANNEL = \"profile_service_channel\"\n        private const val STATUS_CHANNEL = \"profile_status_channel\"\n        private const val RESULT_CHANNEL = \"profile_result_channel\"\n    }\n\n    override fun onBind(intent: Intent?): IBinder {\n        return Binder()\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/RemoteService.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.Intent\nimport android.os.IBinder\nimport yos.clash.material.service.remote.IClashManager\nimport yos.clash.material.service.remote.IRemoteService\nimport yos.clash.material.service.remote.IProfileManager\nimport yos.clash.material.service.remote.wrap\nimport yos.clash.material.service.util.cancelAndJoinBlocking\n\nclass RemoteService : BaseService(), IRemoteService {\n    private val binder = this.wrap()\n\n    private var clash: ClashManager? = null\n    private var profile: ProfileManager? = null\n    private var clashBinder: IClashManager? = null\n    private var profileBinder: IProfileManager? = null\n\n    override fun onCreate() {\n        super.onCreate()\n\n        clash = ClashManager(this)\n        profile = ProfileManager(this)\n        clashBinder = clash?.wrap() as IClashManager?\n        profileBinder = profile?.wrap() as IProfileManager?\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n\n        clash?.cancelAndJoinBlocking()\n        profile?.cancelAndJoinBlocking()\n    }\n\n    override fun onBind(intent: Intent?): IBinder {\n        return binder\n    }\n\n    override fun clash(): IClashManager {\n        return clashBinder!!\n    }\n\n    override fun profile(): IProfileManager {\n        return profileBinder!!\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/StatusProvider.kt",
    "content": "package yos.clash.material.service\n\nimport android.content.ContentProvider\nimport android.content.ContentValues\nimport android.database.Cursor\nimport android.net.Uri\nimport android.os.Bundle\nimport yos.clash.material.common.Global\n\nclass StatusProvider : ContentProvider() {\n    override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {\n        return when (method) {\n            METHOD_CURRENT_PROFILE -> {\n                return if (serviceRunning)\n                    Bundle().apply {\n                        putString(\"name\", currentProfile)\n                    }\n                else\n                    null\n            }\n            else -> super.call(method, arg, extras)\n        }\n    }\n\n    override fun insert(uri: Uri, values: ContentValues?): Uri? {\n        throw IllegalArgumentException(\"Stub!\")\n    }\n\n    override fun query(\n        uri: Uri,\n        projection: Array<out String>?,\n        selection: String?,\n        selectionArgs: Array<out String>?,\n        sortOrder: String?\n    ): Cursor? {\n        throw IllegalArgumentException(\"Stub!\")\n    }\n\n    override fun update(\n        uri: Uri,\n        values: ContentValues?,\n        selection: String?,\n        selectionArgs: Array<out String>?\n    ): Int {\n        throw IllegalArgumentException(\"Stub!\")\n    }\n\n    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {\n        throw IllegalArgumentException(\"Stub!\")\n    }\n\n    override fun getType(uri: Uri): String? {\n        throw IllegalArgumentException(\"Stub!\")\n    }\n\n    override fun onCreate(): Boolean {\n        return true\n    }\n\n    companion object {\n        const val METHOD_CURRENT_PROFILE = \"currentProfile\"\n\n        private const val CLASH_SERVICE_RUNNING_FILE = \"service_running.lock\"\n\n        var serviceRunning: Boolean = false\n            set(value) {\n                field = value\n\n                shouldStartClashOnBoot = value\n            }\n        var shouldStartClashOnBoot: Boolean\n            get() = Global.application.filesDir.resolve(CLASH_SERVICE_RUNNING_FILE).exists()\n            set(value) {\n                Global.application.filesDir.resolve(CLASH_SERVICE_RUNNING_FILE).apply {\n                    if (value)\n                        createNewFile()\n                    else\n                        delete()\n                }\n            }\n        var currentProfile: String? = null\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/TunService.kt",
    "content": "package yos.clash.material.service\n\nimport android.annotation.TargetApi\nimport android.app.PendingIntent\nimport android.content.Intent\nimport android.net.ProxyInfo\nimport android.net.VpnService\nimport android.os.Build\nimport yos.clash.material.common.compat.pendingIntentFlags\nimport yos.clash.material.common.constants.Components\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.service.clash.clashRuntime\nimport yos.clash.material.service.clash.module.*\nimport yos.clash.material.service.model.AccessControlMode\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.cancelAndJoinBlocking\nimport yos.clash.material.service.util.parseCIDR\nimport yos.clash.material.service.util.sendClashStarted\nimport yos.clash.material.service.util.sendClashStopped\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.selects.select\n\nclass TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.Default) {\n    private val self: TunService\n        get() = this\n\n    private var reason: String? = null\n\n    private val runtime = clashRuntime {\n        val store = ServiceStore(self)\n\n        val close = install(CloseModule(self))\n        val tun = install(TunModule(self))\n        val config = install(ConfigurationModule(self))\n        val network = install(NetworkObserveModule(self))\n        val sideload = install(SideloadDatabaseModule(self))\n\n        if (store.dynamicNotification)\n            install(DynamicNotificationModule(self))\n        else\n            install(StaticNotificationModule(self))\n\n        install(AppListCacheModule(self))\n        install(TimeZoneModule(self))\n        install(SuspendModule(self))\n\n        try {\n            tun.open()\n\n            while (isActive) {\n                val quit = select<Boolean> {\n                    close.onEvent {\n                        true\n                    }\n                    config.onEvent {\n                        reason = it.message\n\n                        true\n                    }\n                    sideload.onEvent {\n                        reason = it.message\n\n                        true\n                    }\n                    network.onEvent { n ->\n                        if (Build.VERSION.SDK_INT in 22..28) @TargetApi(22) {\n                            setUnderlyingNetworks(n?.let { arrayOf(it) })\n                        }\n\n                        false\n                    }\n                }\n\n                if (quit) break\n            }\n        } catch (e: Exception) {\n            Log.e(\"Create clash runtime: ${e.message}\", e)\n\n            reason = e.message\n        } finally {\n            withContext(NonCancellable) {\n                tun.close()\n\n                stopSelf()\n            }\n        }\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n\n        if (StatusProvider.serviceRunning)\n            return stopSelf()\n\n        StatusProvider.serviceRunning = true\n\n        StaticNotificationModule.createNotificationChannel(this)\n        StaticNotificationModule.notifyLoadingNotification(this)\n\n        runtime.launch()\n    }\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {\n        sendClashStarted()\n\n        return super.onStartCommand(intent, flags, startId)\n    }\n\n    override fun onDestroy() {\n        TunModule.requestStop()\n\n        StatusProvider.serviceRunning = false\n\n        sendClashStopped(reason)\n\n        cancelAndJoinBlocking()\n\n        Log.i(\"TunService destroyed: ${reason ?: \"successfully\"}\")\n\n        super.onDestroy()\n    }\n\n    override fun onTrimMemory(level: Int) {\n        super.onTrimMemory(level)\n\n        runtime.requestGc()\n    }\n\n    private fun TunModule.open() {\n        val store = ServiceStore(self)\n\n        val device = with(Builder()) {\n            // Interface address\n            addAddress(TUN_GATEWAY, TUN_SUBNET_PREFIX)\n\n            // Route\n            if (store.bypassPrivateNetwork) {\n                resources.getStringArray(R.array.bypass_private_route).map(::parseCIDR).forEach {\n                    addRoute(it.ip, it.prefix)\n                }\n\n                // Route of virtual DNS\n                addRoute(TUN_DNS, 32)\n            } else {\n                addRoute(NET_ANY, 0)\n            }\n\n            // Access Control\n            when (store.accessControlMode) {\n                AccessControlMode.AcceptAll -> Unit\n                AccessControlMode.AcceptSelected -> {\n                    (store.accessControlPackages + packageName).forEach {\n                        runCatching { addAllowedApplication(it) }\n                    }\n                }\n                AccessControlMode.DenySelected -> {\n                    (store.accessControlPackages - packageName).forEach {\n                        runCatching { addDisallowedApplication(it) }\n                    }\n                }\n            }\n\n            // Blocking\n            setBlocking(false)\n\n            // Mtu\n            setMtu(TUN_MTU)\n\n            // Session Name\n            setSession(\"Clash\")\n\n            // Virtual Dns Server\n            addDnsServer(TUN_DNS)\n\n            // Open MainActivity\n            setConfigureIntent(\n                PendingIntent.getActivity(\n                    self,\n                    R.id.nf_vpn_status,\n                    Intent().setComponent(Components.MAIN_ACTIVITY),\n                    pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT)\n                )\n            )\n\n            // Metered\n            if (Build.VERSION.SDK_INT >= 29) {\n                setMetered(false)\n            }\n\n            // System Proxy\n            if (Build.VERSION.SDK_INT >= 29 && store.systemProxy) {\n                listenHttp()?.let {\n                    setHttpProxy(\n                        ProxyInfo.buildDirectProxy(\n                            it.address.hostAddress,\n                            it.port,\n                            HTTP_PROXY_BLACK_LIST + if (store.bypassPrivateNetwork) HTTP_PROXY_LOCAL_LIST else emptyList()\n                        )\n                    )\n                }\n            }\n\n            if (store.allowBypass) {\n                allowBypass()\n            }\n\n            TunModule.TunDevice(\n                fd = establish()?.detachFd()\n                    ?: throw NullPointerException(\"Establish VPN rejected by system\"),\n                gateway = \"$TUN_GATEWAY/$TUN_SUBNET_PREFIX\",\n                portal = TUN_PORTAL,\n                dns = if (store.dnsHijacking) NET_ANY else TUN_DNS,\n            )\n        }\n\n        attach(device)\n    }\n\n    companion object {\n        private const val TUN_MTU = 9000\n        private const val TUN_SUBNET_PREFIX = 30\n        private const val TUN_GATEWAY = \"172.19.0.1\"\n        private const val TUN_PORTAL = \"172.19.0.2\"\n        private const val TUN_DNS = TUN_PORTAL\n        private const val NET_ANY = \"0.0.0.0\"\n\n        private val HTTP_PROXY_LOCAL_LIST: List<String> = listOf(\n            \"localhost\",\n            \"*.local\",\n            \"127.*\",\n            \"10.*\",\n            \"172.16.*\",\n            \"172.17.*\",\n            \"172.18.*\",\n            \"172.19.*\",\n            \"172.2*\",\n            \"172.30.*\",\n            \"172.31.*\",\n            \"192.168.*\"\n        )\n        private val HTTP_PROXY_BLACK_LIST: List<String> = listOf(\n            \"*zhihu.com\",\n            \"*zhimg.com\",\n            \"*jd.com\",\n            \"100ime-iat-api.xfyun.cn\",\n        )\n    }\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/ClashRuntime.kt",
    "content": "package yos.clash.material.service.clash\n\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport yos.clash.material.service.clash.module.Module\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\n\nprivate val globalLock = Mutex()\n\ninterface ClashRuntimeScope {\n    fun <E, T : Module<E>> install(module: T): T\n}\n\ninterface ClashRuntime {\n    fun launch()\n    fun requestGc()\n}\n\nfun CoroutineScope.clashRuntime(block: suspend ClashRuntimeScope.() -> Unit): ClashRuntime {\n    return object : ClashRuntime {\n        override fun launch() {\n            launch(Dispatchers.IO) {\n                globalLock.withLock {\n                    Log.d(\"ClashRuntime: initialize\")\n\n                    try {\n                        val modules = mutableListOf<Module<*>>()\n\n                        Clash.reset()\n                        Clash.clearOverride(Clash.OverrideSlot.Session)\n\n                        val scope = object : ClashRuntimeScope {\n                            override fun <E, T : Module<E>> install(module: T): T {\n                                launch {\n                                    modules.add(module)\n\n                                    module.execute()\n                                }\n\n                                return module\n                            }\n                        }\n\n                        scope.block()\n\n                        cancel()\n                    } finally {\n                        withContext(NonCancellable) {\n                            Clash.reset()\n                            Clash.clearOverride(Clash.OverrideSlot.Session)\n\n                            Log.d(\"ClashRuntime: destroyed\")\n                        }\n                    }\n                }\n            }\n        }\n\n        override fun requestGc() {\n            Clash.forceGc()\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/AppListCacheModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport android.content.Intent\nimport android.content.pm.PackageInfo\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.delay\nimport java.util.concurrent.TimeUnit\n\nclass AppListCacheModule(service: Service) : Module<Unit>(service) {\n    private fun PackageInfo.uniqueUidName(): String =\n        if (sharedUserId != null && sharedUserId.isNotBlank()) sharedUserId else packageName\n\n    private fun reload() {\n        val packages = service.packageManager.getInstalledPackages(0)\n            .groupBy { it.uniqueUidName() }\n            .map { (_, v) ->\n                val info = v[0]\n\n                if (v.size == 1) {\n                    // Force use package name if only one app in a single sharedUid group\n                    // Example: firefox\n\n                    info.applicationInfo.uid to info.packageName\n                } else {\n                    info.applicationInfo.uid to info.uniqueUidName()\n                }\n            }\n\n        Clash.notifyInstalledAppsChanged(packages)\n\n        Log.d(\"Installed ${packages.size} packages cached\")\n    }\n\n    override suspend fun run() {\n        val packageChanged = receiveBroadcast(false, Channel.CONFLATED) {\n            addAction(Intent.ACTION_PACKAGE_ADDED)\n            addAction(Intent.ACTION_PACKAGE_REMOVED)\n            addDataScheme(\"package\")\n        }\n\n        while (true) {\n            reload()\n\n            packageChanged.receive()\n\n            delay(TimeUnit.SECONDS.toMillis(10))\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/CloseModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.log.Log\n\nclass CloseModule(service: Service) : Module<CloseModule.RequestClose>(service) {\n    object RequestClose\n\n    override suspend fun run() {\n        val broadcasts = receiveBroadcast {\n            addAction(Intents.ACTION_CLASH_REQUEST_STOP)\n        }\n\n        broadcasts.receive()\n\n        Log.d(\"User request close\")\n\n        return enqueueEvent(RequestClose)\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/ConfigurationModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport yos.clash.material.service.StatusProvider\nimport yos.clash.material.service.data.ImportedDao\nimport yos.clash.material.service.data.SelectionDao\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.importedDir\nimport yos.clash.material.service.util.sendProfileLoaded\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.selects.select\nimport java.util.*\n\nclass ConfigurationModule(service: Service) : Module<ConfigurationModule.LoadException>(service) {\n    data class LoadException(val message: String)\n\n    private val store = ServiceStore(service)\n    private val reload = Channel<Unit>(Channel.CONFLATED)\n\n    override suspend fun run() {\n        val broadcasts = receiveBroadcast {\n            addAction(Intents.ACTION_PROFILE_CHANGED)\n            addAction(Intents.ACTION_OVERRIDE_CHANGED)\n        }\n\n        var loaded: UUID? = null\n\n        reload.trySend(Unit)\n\n        while (true) {\n            val changed: UUID? = select {\n                broadcasts.onReceive {\n                    if (it.action == Intents.ACTION_PROFILE_CHANGED)\n                        UUID.fromString(it.getStringExtra(Intents.EXTRA_UUID))\n                    else\n                        null\n                }\n                reload.onReceive {\n                    null\n                }\n            }\n\n            try {\n                val current = store.activeProfile\n                    ?: throw NullPointerException(\"No profile selected\")\n\n                if (current == loaded && changed != null && changed != loaded)\n                    continue\n\n                loaded = current\n\n                val active = ImportedDao().queryByUUID(current)\n                    ?: throw NullPointerException(\"No profile selected\")\n\n                Clash.load(service.importedDir.resolve(active.uuid.toString())).await()\n\n                val remove = SelectionDao().querySelections(active.uuid)\n                    .filterNot { Clash.patchSelector(it.proxy, it.selected) }\n                    .map { it.proxy }\n\n                SelectionDao().removeSelections(active.uuid, remove)\n\n                StatusProvider.currentProfile = active.name\n\n                service.sendProfileLoaded(current)\n\n                Log.d(\"Profile ${active.name} loaded\")\n            } catch (e: Exception) {\n                return enqueueEvent(LoadException(e.message ?: \"Unknown\"))\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/DynamicNotificationModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.content.Intent\nimport android.content.pm.ServiceInfo\nimport android.os.Build\nimport android.os.PowerManager\nimport androidx.core.app.NotificationCompat\nimport androidx.core.content.getSystemService\nimport yos.clash.material.common.compat.getColorCompat\nimport yos.clash.material.common.compat.pendingIntentFlags\nimport yos.clash.material.common.constants.Components\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.util.ticker\nimport com.github.kr328.clash.core.Clash\nimport com.github.kr328.clash.core.util.trafficDownload\nimport com.github.kr328.clash.core.util.trafficUpload\nimport yos.clash.material.service.R\nimport yos.clash.material.service.StatusProvider\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.selects.select\nimport java.util.concurrent.TimeUnit\n\nclass DynamicNotificationModule(service: Service) : Module<Unit>(service) {\n    private val builder = NotificationCompat.Builder(service, StaticNotificationModule.CHANNEL_ID)\n        .setSmallIcon(R.drawable.ic_logo_service)\n        .setOngoing(true)\n        .setColor(service.getColorCompat(R.color.color_clash))\n        .setOnlyAlertOnce(true)\n        .setShowWhen(false)\n        .setContentTitle(\"Not Selected\")\n        .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)\n        .setContentIntent(\n            PendingIntent.getActivity(\n                service,\n                R.id.nf_clash_status,\n                Intent().setComponent(Components.MAIN_ACTIVITY)\n                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP),\n                pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT)\n            )\n        )\n\n    private fun update() {\n        val now = Clash.queryTrafficNow()\n        val total = Clash.queryTrafficTotal()\n\n        val uploading = now.trafficUpload()\n        val downloading = now.trafficDownload()\n        val uploaded = total.trafficUpload()\n        val downloaded = total.trafficDownload()\n\n        val notification = builder\n            .setContentText(\n                service.getString(\n                    R.string.clash_notification_content,\n                    \"$uploading/s\", \"$downloading/s\"\n                )\n            )\n            .setSubText(\n                service.getString(\n                    R.string.clash_notification_content,\n                    uploaded, downloaded\n                )\n            )\n            .build()\n\n        if (Build.VERSION.SDK_INT >= 34) {\n            service.startForeground(\n                R.id.nf_clash_status, notification,\n                ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE\n            )\n        } else {\n            service.startForeground(R.id.nf_clash_status, notification)\n        }\n    }\n\n    override suspend fun run() = coroutineScope {\n        var shouldUpdate = service.getSystemService<PowerManager>()?.isInteractive ?: true\n\n        val screenToggle = receiveBroadcast(false, Channel.CONFLATED) {\n            addAction(Intent.ACTION_SCREEN_ON)\n            addAction(Intent.ACTION_SCREEN_OFF)\n        }\n\n        val profileLoaded = receiveBroadcast(capacity = Channel.CONFLATED) {\n            addAction(Intents.ACTION_PROFILE_LOADED)\n        }\n\n        val ticker = ticker(TimeUnit.SECONDS.toMillis(1))\n\n        while (true) {\n            select<Unit> {\n                screenToggle.onReceive {\n                    when (it.action) {\n                        Intent.ACTION_SCREEN_ON ->\n                            shouldUpdate = true\n                        Intent.ACTION_SCREEN_OFF ->\n                            shouldUpdate = false\n                    }\n                }\n                profileLoaded.onReceive {\n                    builder.setContentTitle(StatusProvider.currentProfile ?: \"Not selected\")\n                }\n                if (shouldUpdate) {\n                    ticker.onReceive {\n                        update()\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/Module.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport yos.clash.material.common.constants.Permissions\nimport yos.clash.material.common.log.Log\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.channels.ReceiveChannel\nimport kotlinx.coroutines.selects.SelectClause1\nimport kotlinx.coroutines.withContext\n\nabstract class Module<E>(val service: Service) {\n    private val events: Channel<E> = Channel(Channel.UNLIMITED)\n    private val receivers: MutableList<BroadcastReceiver> = mutableListOf()\n\n    val onEvent: SelectClause1<E>\n        get() = events.onReceive\n\n    protected suspend fun enqueueEvent(event: E) {\n        events.send(event)\n    }\n\n    protected fun receiveBroadcast(\n        requireSelf: Boolean = true,\n        capacity: Int = Channel.UNLIMITED,\n        configure: IntentFilter.() -> Unit\n    ): ReceiveChannel<Intent> {\n        val filter = IntentFilter().apply(configure)\n        val channel = Channel<Intent>(capacity)\n        val receiver = object : BroadcastReceiver() {\n            override fun onReceive(context: Context?, intent: Intent?) {\n                if (context == null || intent == null) {\n                    channel.close()\n\n                    return\n                }\n\n                channel.trySend(intent)\n            }\n        }\n\n        if (requireSelf) {\n            service.registerReceiver(receiver, filter, Permissions.RECEIVE_SELF_BROADCASTS, null)\n        } else {\n            service.registerReceiver(receiver, filter)\n        }\n\n        receivers.add(receiver)\n\n        return channel\n    }\n\n    suspend fun execute() {\n        val moduleName = this.javaClass.simpleName\n\n        try {\n            Log.d(\"$moduleName: initialize\")\n\n            run()\n        } finally {\n            withContext(NonCancellable) {\n                receivers.forEach {\n                    it.onReceive(null, null)\n\n                    service.unregisterReceiver(it)\n                }\n\n                Log.d(\"$moduleName: destroyed\")\n            }\n        }\n    }\n\n    protected abstract suspend fun run()\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/NetworkObserveModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport android.net.*\nimport android.os.Build\nimport androidx.core.content.getSystemService\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport yos.clash.material.service.util.resolvePrimaryDns\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.channels.trySendBlocking\nimport kotlinx.coroutines.withContext\n\nclass NetworkObserveModule(service: Service) : Module<Network?>(service) {\n    private data class Action(val type: Type, val network: Network) {\n        enum class Type { Available, Lost, Changed }\n    }\n\n    private val connectivity = service.getSystemService<ConnectivityManager>()!!\n    private val actions = Channel<Action>(Channel.UNLIMITED)\n    private val request = NetworkRequest.Builder().apply {\n        addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)\n        addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)\n        addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)\n    }.build()\n    private val callback = object : ConnectivityManager.NetworkCallback() {\n        override fun onAvailable(network: Network) {\n            actions.trySendBlocking(Action(Action.Type.Available, network))\n        }\n\n        override fun onLost(network: Network) {\n            actions.trySendBlocking(Action(Action.Type.Lost, network))\n        }\n\n        override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {\n            actions.trySendBlocking(Action(Action.Type.Changed, network))\n        }\n    }\n\n    override suspend fun run() {\n        try {\n            connectivity.registerNetworkCallback(request, callback)\n        } catch (e: Exception) {\n            Log.w(\"Observe network failed: $e\", e)\n\n            return\n        }\n\n        try {\n            val networks = mutableSetOf<Network>()\n\n            while (true) {\n                val action = actions.receive()\n\n                val resolveDefault = when (action.type) {\n                    Action.Type.Available -> {\n                        networks.add(action.network)\n\n                        true\n                    }\n                    Action.Type.Lost -> {\n                        networks.remove(action.network)\n\n                        true\n                    }\n                    Action.Type.Changed -> {\n                        false\n                    }\n                }\n\n                val dns = networks.mapNotNull {\n                    connectivity.resolvePrimaryDns(it)\n                }\n\n                Clash.notifyDnsChanged(dns)\n\n                Log.d(\"DNS: $dns\")\n\n                if (resolveDefault) {\n                    val network = networks.maxByOrNull { net ->\n                        connectivity.getNetworkCapabilities(net)?.let { cap ->\n                            TRANSPORT_PRIORITY.indexOfFirst { cap.hasTransport(it) }\n                        } ?: -1\n                    }\n\n                    enqueueEvent(network)\n\n                    Log.d(\"Network: $network of $networks\")\n                }\n            }\n        } finally {\n            withContext(NonCancellable) {\n                enqueueEvent(null)\n\n                Clash.notifyDnsChanged(emptyList())\n\n                runCatching {\n                    connectivity.unregisterNetworkCallback(callback)\n                }\n            }\n        }\n    }\n\n    companion object {\n        private val TRANSPORT_PRIORITY = sequence {\n            yield(NetworkCapabilities.TRANSPORT_CELLULAR)\n\n            if (Build.VERSION.SDK_INT >= 27) {\n                yield(NetworkCapabilities.TRANSPORT_LOWPAN)\n            }\n\n            yield(NetworkCapabilities.TRANSPORT_BLUETOOTH)\n\n            if (Build.VERSION.SDK_INT >= 26) {\n                yield(NetworkCapabilities.TRANSPORT_WIFI_AWARE)\n            }\n\n            yield(NetworkCapabilities.TRANSPORT_WIFI)\n\n            if (Build.VERSION.SDK_INT >= 31) {\n                yield(NetworkCapabilities.TRANSPORT_USB)\n            }\n\n            yield(NetworkCapabilities.TRANSPORT_ETHERNET)\n        }.toList()\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/SideloadDatabaseModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport android.content.Intent\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport yos.clash.material.service.sideload.readGeoipDatabaseFrom\nimport yos.clash.material.service.store.ServiceStore\nimport yos.clash.material.service.util.packageName\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.selects.select\nimport java.io.FileNotFoundException\nimport java.io.IOException\n\nclass SideloadDatabaseModule(service: Service) :\n    Module<SideloadDatabaseModule.LoadException>(service) {\n    data class LoadException(val message: String)\n\n    private val store = ServiceStore(service)\n\n    private var current: String = \"\"\n\n    override suspend fun run() {\n        val packagesChanged = receiveBroadcast(false) {\n            addAction(Intent.ACTION_PACKAGE_ADDED)\n            addAction(Intent.ACTION_PACKAGE_REPLACED)\n            addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)\n            addDataScheme(\"package\")\n        }\n        val profileChanged = receiveBroadcast(capacity = Channel.CONFLATED) {\n            addAction(Intents.ACTION_PROFILE_CHANGED)\n        }\n        val initial = Channel<Unit>(1).apply { send(Unit) }\n\n        while (true) {\n            val (reload, force) = select<Pair<Boolean, Boolean>> {\n                packagesChanged.onReceive {\n                    when (it.action) {\n                        Intent.ACTION_PACKAGE_ADDED ->\n                            (it.packageName == store.sideloadGeoip) to true\n                        Intent.ACTION_PACKAGE_REPLACED ->\n                            (it.packageName == current) to true\n                        Intent.ACTION_PACKAGE_FULLY_REMOVED ->\n                            (it.packageName == current) to true\n                        else -> false to false\n                    }\n                }\n                profileChanged.onReceive {\n                    true to false\n                }\n                initial.onReceive {\n                    true to true\n                }\n            }\n\n            if (!reload) continue\n\n            val pkg = store.sideloadGeoip\n\n            try {\n                if (!force && pkg == current)\n                    continue\n\n                current = pkg\n\n                if (pkg.isNotBlank()) {\n                    val data = service.readGeoipDatabaseFrom(pkg)\n\n                    Clash.installSideloadGeoip(data)\n\n                    if (data != null) {\n                        Log.d(\"Sideload geoip loaded, pkg = $pkg\")\n                    } else {\n                        Log.d(\"Sideload geoip not found\")\n                    }\n                }\n            } catch (e: FileNotFoundException) {\n                return enqueueEvent(LoadException(\"file $pkg/assets/${e.message} not found\"))\n            } catch (e: IOException) {\n                return enqueueEvent(LoadException(\"read data from $pkg: ${e.message}\"))\n            } catch (e: Exception) {\n                return enqueueEvent(LoadException(e.toString()))\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/StaticNotificationModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.content.Intent\nimport android.content.pm.ServiceInfo\nimport android.os.Build\nimport androidx.core.app.NotificationChannelCompat\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport yos.clash.material.common.compat.getColorCompat\nimport yos.clash.material.common.compat.pendingIntentFlags\nimport yos.clash.material.common.constants.Components\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.service.R\nimport yos.clash.material.service.StatusProvider\nimport kotlinx.coroutines.channels.Channel\n\nclass StaticNotificationModule(service: Service) : Module<Unit>(service) {\n    private val builder = NotificationCompat.Builder(service, CHANNEL_ID)\n        .setSmallIcon(R.drawable.ic_logo_service)\n        .setOngoing(true)\n        .setColor(service.getColorCompat(R.color.color_clash))\n        .setOnlyAlertOnce(true)\n        .setShowWhen(false)\n        .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)\n        .setContentIntent(\n            PendingIntent.getActivity(\n                service,\n                R.id.nf_clash_status,\n                Intent().setComponent(Components.MAIN_ACTIVITY)\n                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP),\n                pendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT)\n            )\n        )\n\n    override suspend fun run() {\n        val loaded = receiveBroadcast(capacity = Channel.CONFLATED) {\n            addAction(Intents.ACTION_PROFILE_LOADED)\n        }\n\n        while (true) {\n            loaded.receive()\n\n            val profileName = StatusProvider.currentProfile ?: \"Not selected\"\n\n            val notification = builder\n                .setContentTitle(profileName)\n                .setContentText(service.getText(R.string.running))\n                .build()\n\n            if (Build.VERSION.SDK_INT >= 34) {\n                service.startForeground(\n                    R.id.nf_clash_status, notification,\n                    ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE\n                )\n            } else {\n                service.startForeground(R.id.nf_clash_status, notification)\n            }\n        }\n    }\n\n    companion object {\n        const val CHANNEL_ID = \"clash_status_channel\"\n\n        fun createNotificationChannel(service: Service) {\n            NotificationManagerCompat.from(service).createNotificationChannel(\n                NotificationChannelCompat.Builder(\n                    CHANNEL_ID,\n                    NotificationManagerCompat.IMPORTANCE_LOW\n                ).setName(service.getText(R.string.clash_service_status_channel)).build()\n            )\n        }\n\n        fun notifyLoadingNotification(service: Service) {\n            val notification =\n                NotificationCompat.Builder(service, CHANNEL_ID)\n                    .setSmallIcon(R.drawable.ic_logo_service)\n                    .setOngoing(true)\n                    .setColor(service.getColorCompat(R.color.color_clash))\n                    .setOnlyAlertOnce(true)\n                    .setShowWhen(false)\n                    .setContentTitle(service.getText(R.string.loading))\n                    .build()\n\n            if (Build.VERSION.SDK_INT >= 34) {\n                service.startForeground(\n                    R.id.nf_clash_status, notification,\n                    ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE\n                )\n            } else {\n                service.startForeground(R.id.nf_clash_status, notification)\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/SuspendModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport android.content.Intent\nimport android.os.PowerManager\nimport androidx.core.content.getSystemService\nimport yos.clash.material.common.log.Log\nimport com.github.kr328.clash.core.Clash\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.withContext\n\nclass SuspendModule(service: Service) : Module<Unit>(service) {\n    override suspend fun run() {\n        val interactive = service.getSystemService<PowerManager>()?.isInteractive ?: true\n\n        Clash.suspendCore(!interactive)\n\n        val screenToggle = receiveBroadcast(false, Channel.CONFLATED) {\n            addAction(Intent.ACTION_SCREEN_ON)\n            addAction(Intent.ACTION_SCREEN_OFF)\n        }\n\n        try {\n            while (true) {\n                when (screenToggle.receive().action) {\n                    Intent.ACTION_SCREEN_ON -> {\n                        Clash.suspendCore(false)\n\n                        Log.d(\"Clash resumed\")\n                    }\n                    Intent.ACTION_SCREEN_OFF -> {\n                        Clash.suspendCore(true)\n\n                        Log.d(\"Clash suspended\")\n                    }\n                    else -> {\n                        // unreachable\n\n                        Clash.healthCheckAll()\n                    }\n                }\n            }\n        } finally {\n            withContext(NonCancellable) {\n                Clash.suspendCore(false)\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/TimeZoneModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.app.Service\nimport android.content.Intent\nimport com.github.kr328.clash.core.Clash\nimport java.util.*\n\nclass TimeZoneModule(service: Service) : Module<Unit>(service) {\n    override suspend fun run() {\n        val timeZones = receiveBroadcast {\n            addAction(Intent.ACTION_TIMEZONE_CHANGED)\n        }\n\n        while (true) {\n            val timeZone = TimeZone.getDefault()\n\n            Clash.notifyTimeZoneChanged(timeZone.id, timeZone.rawOffset)\n\n            timeZones.receive()\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/clash/module/TunModule.kt",
    "content": "package yos.clash.material.service.clash.module\n\nimport android.net.ConnectivityManager\nimport android.net.VpnService\nimport android.os.Build\nimport androidx.core.content.getSystemService\nimport com.github.kr328.clash.core.Clash\nimport com.github.kr328.clash.core.util.parseInetSocketAddress\nimport kotlinx.coroutines.NonCancellable\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.withContext\nimport java.net.InetSocketAddress\nimport java.security.SecureRandom\n\nclass TunModule(private val vpn: VpnService) : Module<Unit>(vpn) {\n    data class TunDevice(\n        val fd: Int,\n        val gateway: String,\n        val portal: String,\n        val dns: String,\n    )\n\n    private val connectivity = service.getSystemService<ConnectivityManager>()!!\n    private val close = Channel<Unit>(Channel.CONFLATED)\n\n    private fun queryUid(\n        protocol: Int,\n        source: InetSocketAddress,\n        target: InetSocketAddress,\n    ): Int {\n        if (Build.VERSION.SDK_INT < 29)\n            return -1\n\n        return runCatching { connectivity.getConnectionOwnerUid(protocol, source, target) }\n            .getOrElse { -1 }\n    }\n\n    override suspend fun run() {\n        try {\n            return close.receive()\n        } finally {\n            withContext(NonCancellable) {\n                requestStop()\n            }\n        }\n    }\n\n    fun listenHttp(): InetSocketAddress? {\n        val r = { 1 + random.nextInt(199) }\n        val listenAt = \"127.${r()}.${r()}.${r()}:0\"\n        val address = Clash.startHttp(listenAt)\n\n        return address?.let(::parseInetSocketAddress)\n    }\n\n    fun attach(device: TunDevice) {\n        Clash.startTun(\n            fd = device.fd,\n            gateway = device.gateway,\n            portal = device.portal,\n            dns = device.dns,\n            markSocket = vpn::protect,\n            querySocketUid = this::queryUid\n        )\n    }\n\n    suspend fun close() {\n        close.send(Unit)\n    }\n\n    companion object {\n        private val random = SecureRandom()\n\n        fun requestStop() {\n            Clash.stopHttp()\n            Clash.stopTun()\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/Converters.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.TypeConverter\nimport yos.clash.material.service.model.Profile\nimport java.util.*\n\nclass Converters {\n    @TypeConverter\n    fun fromUUID(uuid: UUID): String {\n        return uuid.toString()\n    }\n\n    @TypeConverter\n    fun toUUID(uuid: String): UUID {\n        return UUID.fromString(uuid)\n    }\n\n    @TypeConverter\n    fun fromProfileType(type: Profile.Type): String {\n        return type.name\n    }\n\n    @TypeConverter\n    fun toProfileType(type: String): Profile.Type {\n        return Profile.Type.valueOf(type)\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/Daos.kt",
    "content": "package yos.clash.material.service.data\n\nfun ImportedDao(): ImportedDao {\n    return Database.database.openImportedDao()\n}\n\nfun PendingDao(): PendingDao {\n    return Database.database.openPendingDao()\n}\n\nfun SelectionDao(): SelectionDao {\n    return Database.database.openSelectionProxyDao()\n}\n\nfun ProviderMoreInfoDao(): ProviderMoreInfoDao {\n    return Database.database.openSubscriptionUserInfoDao()\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/Database.kt",
    "content": "package yos.clash.material.service.data\n\nimport android.content.Context\nimport androidx.room.Room\nimport androidx.room.RoomDatabase\nimport yos.clash.material.common.Global\nimport yos.clash.material.service.data.migrations.LEGACY_MIGRATION\nimport yos.clash.material.service.data.migrations.MIGRATIONS\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport java.lang.ref.SoftReference\nimport androidx.room.Database as DB\n\n@DB(\n    version = 1,\n    entities = [Imported::class, Pending::class, Selection::class, ProviderMoreInfo::class],\n    exportSchema = false,\n)\nabstract class Database : RoomDatabase() {\n    abstract fun openImportedDao(): ImportedDao\n    abstract fun openPendingDao(): PendingDao\n    abstract fun openSelectionProxyDao(): SelectionDao\n    abstract fun openSubscriptionUserInfoDao(): ProviderMoreInfoDao\n\n    companion object {\n        val database: Database\n            @Synchronized get() {\n                return softDatabase.get() ?: open(Global.application).apply {\n                    softDatabase = SoftReference(this)\n                }\n            }\n\n        private var softDatabase: SoftReference<Database?> = SoftReference(null)\n\n        private fun open(context: Context): Database {\n            return Room.databaseBuilder(\n                context.applicationContext,\n                Database::class.java,\n                \"profiles\"\n            ).addMigrations(*MIGRATIONS).build()\n        }\n\n        init {\n            Global.launch(Dispatchers.IO) {\n                LEGACY_MIGRATION(Global.application)\n            }\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/Imported.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.TypeConverters\nimport yos.clash.material.service.model.Profile\nimport java.util.*\n\n@Entity(tableName = \"imported\", primaryKeys = [\"uuid\"])\n@TypeConverters(Converters::class)\ndata class Imported(\n    @ColumnInfo(name = \"uuid\") val uuid: UUID,\n    @ColumnInfo(name = \"name\") val name: String,\n    @ColumnInfo(name = \"type\") val type: Profile.Type,\n    @ColumnInfo(name = \"source\") val source: String,\n    @ColumnInfo(name = \"interval\") val interval: Long,\n    @ColumnInfo(name = \"createdAt\") val createdAt: Long,\n)"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/ImportedDao.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.*\nimport java.util.*\n\n@Dao\n@TypeConverters(Converters::class)\ninterface ImportedDao {\n    @Query(\"SELECT * FROM imported WHERE uuid = :uuid\")\n    suspend fun queryByUUID(uuid: UUID): Imported?\n\n    @Query(\"SELECT uuid FROM imported ORDER BY createdAt\")\n    suspend fun queryAllUUIDs(): List<UUID>\n\n    @Insert(onConflict = OnConflictStrategy.ABORT)\n    suspend fun insert(imported: Imported): Long\n\n    @Update(onConflict = OnConflictStrategy.ABORT)\n    suspend fun update(imported: Imported)\n\n    @Query(\"DELETE FROM imported WHERE uuid = :uuid\")\n    suspend fun remove(uuid: UUID)\n\n    @Query(\"SELECT EXISTS(SELECT 1 FROM imported WHERE uuid = :uuid)\")\n    suspend fun exists(uuid: UUID): Boolean\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/Pending.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.TypeConverters\nimport yos.clash.material.service.model.Profile\nimport java.util.*\n\n@Entity(tableName = \"pending\", primaryKeys = [\"uuid\"])\n@TypeConverters(Converters::class)\ndata class Pending(\n    @ColumnInfo(name = \"uuid\") val uuid: UUID,\n    @ColumnInfo(name = \"name\") val name: String,\n    @ColumnInfo(name = \"type\") val type: Profile.Type,\n    @ColumnInfo(name = \"source\") val source: String,\n    @ColumnInfo(name = \"interval\") val interval: Long,\n    @ColumnInfo(name = \"createdAt\") val createdAt: Long = System.currentTimeMillis(),\n)"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/PendingDao.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.*\nimport java.util.*\n\n@Dao\n@TypeConverters(Converters::class)\ninterface PendingDao {\n    @Query(\"SELECT * FROM pending WHERE uuid = :uuid\")\n    suspend fun queryByUUID(uuid: UUID): Pending?\n\n    @Query(\"DELETE FROM pending WHERE uuid = :uuid\")\n    suspend fun remove(uuid: UUID)\n\n    @Query(\"SELECT EXISTS(SELECT 1 FROM pending WHERE uuid = :uuid)\")\n    suspend fun exists(uuid: UUID): Boolean\n\n    @Query(\"SELECT uuid FROM pending ORDER BY createdAt\")\n    suspend fun queryAllUUIDs(): List<UUID>\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insert(pending: Pending)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun update(pending: Pending)\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/ProviderMoreInfo.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\nimport androidx.room.TypeConverters\nimport java.util.*\n\n@Entity(\n    tableName = \"provider_more_info\",\n    foreignKeys = [ForeignKey(\n        entity = Imported::class,\n        childColumns = [\"uuid\"],\n        parentColumns = [\"uuid\"],\n        onDelete = ForeignKey.CASCADE,\n        onUpdate = ForeignKey.CASCADE\n    )],\n    primaryKeys = [\"uuid\"]\n)\n@TypeConverters(Converters::class)\ndata class ProviderMoreInfo(\n    @ColumnInfo(name = \"uuid\") val uuid: UUID,\n    @ColumnInfo(name = \"upload\") val upload: Long,\n    @ColumnInfo(name = \"download\") val download: Long,\n    @ColumnInfo(name = \"total\") val total: Long,\n    @ColumnInfo(name = \"expire\") val expire: Long,\n)"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/ProviderMoreInfoDao.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.*\nimport java.util.*\n\n@Dao\n@TypeConverters(Converters::class)\ninterface ProviderMoreInfoDao {\n    @Query(\"SELECT * FROM provider_more_info WHERE uuid = :uuid\")\n    suspend fun queryByUUID(uuid: UUID): ProviderMoreInfo?\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun setInfo(info: ProviderMoreInfo)\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/Selection.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\nimport androidx.room.TypeConverters\nimport java.util.*\n\n@Entity(\n    tableName = \"selections\",\n    foreignKeys = [ForeignKey(\n        entity = Imported::class,\n        childColumns = [\"uuid\"],\n        parentColumns = [\"uuid\"],\n        onDelete = ForeignKey.CASCADE,\n        onUpdate = ForeignKey.CASCADE\n    )],\n    primaryKeys = [\"uuid\", \"proxy\"]\n)\n@TypeConverters(Converters::class)\ndata class Selection(\n    @ColumnInfo(name = \"uuid\") val uuid: UUID,\n    @ColumnInfo(name = \"proxy\") val proxy: String,\n    @ColumnInfo(name = \"selected\") val selected: String,\n)"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/SelectionDao.kt",
    "content": "package yos.clash.material.service.data\n\nimport androidx.room.*\nimport java.util.*\n\n@Dao\n@TypeConverters(Converters::class)\ninterface SelectionDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun setSelected(selection: Selection)\n\n    @Query(\"DELETE FROM selections WHERE uuid = :uuid AND proxy = :proxy\")\n    fun removeSelected(uuid: UUID, proxy: String)\n\n    @Query(\"SELECT * FROM selections WHERE uuid = :uuid\")\n    suspend fun querySelections(uuid: UUID): List<Selection>\n\n    @Query(\"DELETE FROM selections WHERE uuid = :uuid AND proxy in (:proxies)\")\n    suspend fun removeSelections(uuid: UUID, proxies: List<String>)\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/migrations/LegacyMigration.kt",
    "content": "@file:Suppress(\"BlockingMethodInNonBlockingContext\")\n\npackage yos.clash.material.service.data.migrations\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport androidx.core.text.isDigitsOnly\nimport yos.clash.material.common.log.Log\nimport yos.clash.material.service.data.Pending\nimport yos.clash.material.service.data.PendingDao\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.service.util.generateProfileUUID\nimport yos.clash.material.service.util.pendingDir\nimport yos.clash.material.service.util.sendProfileChanged\nimport java.io.File\n\ninternal suspend fun migrationFromLegacy(context: Context) {\n    val file = context.getDatabasePath(\"clash-config\")\n\n    if (!file.exists()) {\n        return\n    }\n\n    Log.i(\"Migration from legacy database\")\n\n    try {\n        SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY)\n            .use { db ->\n                val v = db.version\n\n                Log.i(\"Legacy database version = $v\")\n\n                when (v) {\n                    1 -> migrationFromLegacy1(context, db)\n                    2, 3, 4 -> migrationFromLegacy234(context, db, v)\n                }\n            }\n    } catch (e: Exception) {\n        Log.w(\"Migration legacy database: $e\", e)\n    }\n\n    context.deleteDatabase(\"clash-config\")\n\n    Log.i(\"Legacy database migrated\")\n}\n\nprivate suspend fun migrationFromLegacy234(\n    context: Context,\n    legacy: SQLiteDatabase,\n    version: Int,\n) {\n    legacy.query(\n        \"profiles\",\n        arrayOf(\"id\", \"name\", \"type\", \"uri\", if (version == 2) \"update_interval\" else \"interval\"),\n        null,\n        null,\n        null,\n        null,\n        \"id\"\n    ).use { cursor ->\n        val id = cursor.getColumnIndex(\"id\")\n        val name = cursor.getColumnIndex(\"name\")\n        val type = cursor.getColumnIndex(\"type\")\n        val uri = cursor.getColumnIndex(\"uri\")\n        val interval = cursor.getColumnIndex(if (version == 2) \"update_interval\" else \"interval\")\n\n        if (!cursor.moveToFirst())\n            return\n\n        do {\n            val newType = when (cursor.getInt(type)) {\n                1 -> { // TYPE_FILE\n                    Profile.Type.File\n                }\n                2 -> { // TYPE_URL\n                    Profile.Type.Url\n                }\n                3 -> { // TYPE_EXTERNAL\n                    Profile.Type.External\n                }\n                else -> { // unknown\n                    continue\n                }\n            }\n\n            val idValue = cursor.getInt(id)\n            val intervalValue = cursor.getLong(interval)\n\n            val pending = Pending(\n                uuid = generateProfileUUID(),\n                name = cursor.getString(name),\n                type = newType,\n                source = if (newType != Profile.Type.File) cursor.getString(uri) else \"\",\n                interval = if (version == 2) intervalValue * 1000 else intervalValue,\n            )\n\n            val base = context.pendingDir.resolve(pending.uuid.toString())\n\n            base.apply {\n                mkdirs()\n\n                resolve(\"config.yaml\").createNewFile()\n                resolve(\"providers\").mkdir()\n            }\n\n            if (newType == Profile.Type.File) {\n                val legacyFile = context.filesDir.resolve(\"profiles/$idValue.yaml\")\n\n                if (legacyFile.isFile) {\n                    legacyFile.copyTo(base.resolve(\"config.yaml\"), overwrite = true)\n                }\n            }\n\n            PendingDao().insert(pending)\n\n            context.sendProfileChanged(pending.uuid)\n\n            Log.i(\"${pending.name} migrated\")\n        } while (cursor.moveToNext())\n    }\n\n    context.filesDir.resolve(\"profiles\").deleteRecursively()\n    context.filesDir.resolve(\"clash\").listFiles()?.forEach {\n        if (it.name.isDigitsOnly()) {\n            it.deleteRecursively()\n        }\n    }\n}\n\nprivate suspend fun migrationFromLegacy1(context: Context, legacy: SQLiteDatabase) {\n    legacy.query(\n        \"profiles\",\n        arrayOf(\"name\", \"token\", \"id\", \"file\"),\n        null,\n        null,\n        null,\n        null,\n        \"id\",\n    ).use { cursor ->\n        val name = cursor.getColumnIndex(\"name\")\n        val token = cursor.getColumnIndex(\"token\")\n        val file = cursor.getColumnIndex(\"file\")\n\n        if (!cursor.moveToFirst())\n            return\n\n        do {\n            val legacyToken = cursor.getString(token)\n\n            val newType = when {\n                legacyToken.startsWith(\"file|\") -> Profile.Type.File\n                legacyToken.startsWith(\"url|\") -> Profile.Type.Url\n                else -> continue\n            }\n\n            val source = if (newType == Profile.Type.Url) {\n                legacyToken.removePrefix(\"url|\")\n            } else {\n                \"\"\n            }\n\n            val pending = Pending(\n                uuid = generateProfileUUID(),\n                name = cursor.getString(name),\n                type = newType,\n                source = source,\n                interval = 0,\n            )\n\n            val base = context.pendingDir.resolve(pending.uuid.toString())\n\n            base.apply {\n                mkdirs()\n\n                resolve(\"config.yaml\").createNewFile()\n                resolve(\"providers\").mkdir()\n            }\n\n            val legacyFile = File(cursor.getString(file))\n\n            if (newType == Profile.Type.File) {\n                if (legacyFile.isFile) {\n                    legacyFile.copyTo(base.resolve(\"config.yaml\"), overwrite = true)\n                }\n            }\n\n            legacyFile.delete()\n\n            PendingDao().insert(pending)\n\n            context.sendProfileChanged(pending.uuid)\n\n            Log.i(\"${pending.name} migrated\")\n        } while (cursor.moveToNext())\n    }\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/data/migrations/Migrations.kt",
    "content": "package yos.clash.material.service.data.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nprivate val MIGRATION_1_2 = object : Migration(1, 2) {\n    override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"CREATE TABLE `provider_more_info` (`uuid` TEXT NOT NULL, `upload` INTEGER NOT NULL, `download` INTEGER NOT NULL, `total` INTEGER NOT NULL, `expire` INTEGER NOT NULL, PRIMARY KEY(`uuid`), FOREIGN KEY(`uuid`) REFERENCES `imported`(`uuid`) ON UPDATE CASCADE ON DELETE CASCADE )\")\n    }\n}\n\nval MIGRATIONS: Array<Migration> = arrayOf(MIGRATION_1_2)\n\nval LEGACY_MIGRATION = ::migrationFromLegacy"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/Document.kt",
    "content": "package yos.clash.material.service.document\n\ninterface Document {\n    val id: String\n    val name: String\n    val mimeType: String\n    val size: Long\n    val updatedAt: Long\n    val flags: Set<Flag>\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/FileDocument.kt",
    "content": "package yos.clash.material.service.document\n\nimport android.provider.DocumentsContract\nimport java.io.File\n\nclass FileDocument(\n    val file: File,\n    override val flags: Set<Flag>,\n    private val idOverride: String? = null,\n    private val nameOverride: String? = null,\n) : Document {\n    override val id: String\n        get() = idOverride ?: file.name\n    override val name: String\n        get() = nameOverride ?: file.name\n    override val mimeType: String\n        get() = if (file.isDirectory) DocumentsContract.Document.MIME_TYPE_DIR else \"text/plain\"\n    override val size: Long\n        get() = file.length()\n    override val updatedAt: Long\n        get() = file.lastModified()\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/Flag.kt",
    "content": "package yos.clash.material.service.document\n\nenum class Flag {\n    Writable, Deletable, Virtual\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/Path.kt",
    "content": "package yos.clash.material.service.document\n\nimport java.util.*\n\ndata class Path(\n    val uuid: UUID?,\n    val scope: Scope?,\n    val relative: List<String>?\n) {\n    enum class Scope {\n        Configuration, Providers\n    }\n\n    override fun toString(): String {\n        if (uuid == null)\n            return \"/\"\n\n        if (scope == null)\n            return \"/$uuid\"\n\n        val sc = when (scope) {\n            Scope.Configuration -> Paths.CONFIGURATION_ID\n            Scope.Providers -> Paths.PROVIDERS_ID\n        }\n\n        if (relative == null)\n            return \"/$uuid/$sc\"\n\n        return \"/$uuid/$sc/${relative.joinToString(separator = \"/\")}\"\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/Paths.kt",
    "content": "package yos.clash.material.service.document\n\nimport java.util.*\n\nobject Paths {\n    const val CONFIGURATION_ID = \"config.yaml\"\n    const val PROVIDERS_ID = \"providers\"\n\n    fun resolve(path: String): Path {\n        val segments = path.split(\"/\").filter { it.isNotBlank() && it != \".\" && it != \"..\" }\n\n        return when (segments.size) {\n            0 -> Path(\n                uuid = null,\n                scope = null,\n                relative = null,\n            )\n            1 -> Path(\n                uuid = UUID.fromString(segments[0]),\n                scope = null,\n                relative = null,\n            )\n            2 -> Path(\n                uuid = UUID.fromString(segments[0]),\n                scope = when (segments[1]) {\n                    CONFIGURATION_ID -> Path.Scope.Configuration\n                    PROVIDERS_ID -> Path.Scope.Providers\n                    else -> throw IllegalArgumentException(\"unknown scope ${segments[1]}\")\n                },\n                relative = null,\n            )\n            else -> Path(\n                uuid = UUID.fromString(segments[0]),\n                scope = when (segments[1]) {\n                    CONFIGURATION_ID -> Path.Scope.Configuration\n                    PROVIDERS_ID -> Path.Scope.Providers\n                    else -> throw IllegalArgumentException(\"unknown scope ${segments[1]}\")\n                },\n                relative = segments.drop(2),\n            )\n        }\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/Picker.kt",
    "content": "package yos.clash.material.service.document\n\nimport android.content.Context\nimport android.provider.DocumentsContract\nimport yos.clash.material.service.R\nimport yos.clash.material.service.data.ImportedDao\nimport yos.clash.material.service.data.Pending\nimport yos.clash.material.service.data.PendingDao\nimport yos.clash.material.service.model.Profile\nimport yos.clash.material.service.util.importedDir\nimport yos.clash.material.service.util.pendingDir\nimport java.io.FileNotFoundException\nimport java.util.*\n\nclass Picker(private val context: Context) {\n    suspend fun list(path: Path): List<Document> {\n        if (path.uuid == null) {\n            return ImportedDao().queryAllUUIDs().map {\n                pick(path.copy(uuid = it), false)\n            }\n        }\n\n        if (path.scope == null) {\n            return listOf(Path.Scope.Configuration, Path.Scope.Providers).map {\n                pick(path.copy(scope = it), false)\n            }\n        }\n\n        val parent = pick(path, false)\n\n        if (parent !is FileDocument)\n            return emptyList()\n\n        return (parent.file.list() ?: emptyArray()).map {\n            pick(path.copy(relative = (path.relative ?: emptyList()) + it), false)\n        }\n    }\n\n    suspend fun pick(path: Path, writable: Boolean): Document {\n        if (path.uuid == null) {\n            return VirtualDocument(\n                \"\",\n                context.getString(R.string.clash_for_android),\n                DocumentsContract.Document.MIME_TYPE_DIR,\n                0,\n                0,\n                setOf(Flag.Virtual),\n            )\n        }\n\n        if (writable) {\n            cloneToPending(path.uuid)\n        }\n\n        val imported = ImportedDao().queryByUUID(path.uuid)\n        val pending = PendingDao().queryByUUID(path.uuid)\n\n        if (path.scope == null) {\n            if (writable)\n                throw IllegalArgumentException(\"invalid open mode\")\n\n            return VirtualDocument(\n                id = path.uuid.toString(),\n                name = pending?.name ?: imported?.name\n                ?: throw FileNotFoundException(\"profile not found\"),\n                mimeType = DocumentsContract.Document.MIME_TYPE_DIR,\n                size = 0,\n                updatedAt = 0,\n                flags = setOf(Flag.Virtual),\n            )\n        }\n\n        if (path.relative == null) {\n            if (path.scope == Path.Scope.Configuration) {\n                val type = pending?.type ?: imported?.type\n                ?: throw FileNotFoundException(\"profile not found\")\n\n                if (writable && type != Profile.Type.File)\n                    throw IllegalArgumentException(\"invalid open mode\")\n\n                val flags: Set<Flag> = if (type == Profile.Type.Url)\n                    emptySet()\n                else\n                    setOf(Flag.Writable)\n\n                return FileDocument(\n                    file = when {\n                        pending != null -> context.pendingDir.resolve(pending.uuid.toString())\n                        imported != null -> context.importedDir.resolve(imported.uuid.toString())\n                        else -> throw FileNotFoundException(\"profile not found\")\n                    }.resolve(\"config.yaml\"),\n                    flags = flags,\n                    idOverride = Paths.CONFIGURATION_ID,\n                    nameOverride = context.getString(R.string.configuration_yaml)\n                )\n            } else {\n                return FileDocument(\n                    file = when {\n                        pending != null -> context.pendingDir.resolve(pending.uuid.toString())\n                        imported != null -> context.importedDir.resolve(imported.uuid.toString())\n                        else -> throw FileNotFoundException(\"profile not found\")\n                    }.resolve(\"providers\"),\n                    idOverride = Paths.PROVIDERS_ID,\n                    nameOverride = context.getString(R.string.provider_files),\n                    flags = setOf(Flag.Virtual)\n                )\n            }\n        }\n\n        if (path.scope != Path.Scope.Providers)\n            throw FileNotFoundException(\"invalid path\")\n\n        return FileDocument(\n            file = when {\n                pending != null -> context.pendingDir.resolve(pending.uuid.toString())\n                imported != null -> context.importedDir.resolve(imported.uuid.toString())\n                else -> throw FileNotFoundException(\"profile not found\")\n            }.resolve(\"providers\").resolve(path.relative.joinToString(separator = \"/\")),\n            flags = setOf(Flag.Writable, Flag.Deletable)\n        )\n    }\n\n    private suspend fun cloneToPending(uuid: UUID) {\n        if (PendingDao().queryByUUID(uuid) != null)\n            return\n\n        val imported =\n            ImportedDao().queryByUUID(uuid) ?: throw FileNotFoundException(\"profile not found\")\n\n        PendingDao().insert(\n            Pending(\n                imported.uuid,\n                imported.name,\n                imported.type,\n                imported.source,\n                imported.interval\n            )\n        )\n\n        val source = context.importedDir.resolve(uuid.toString())\n        val target = context.pendingDir.resolve(uuid.toString())\n\n        target.deleteRecursively()\n        source.copyRecursively(target)\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/document/VirtualDocument.kt",
    "content": "package yos.clash.material.service.document\n\nclass VirtualDocument(\n    override val id: String,\n    override val name: String,\n    override val mimeType: String,\n    override val size: Long,\n    override val updatedAt: Long,\n    override val flags: Set<Flag>,\n) : Document\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/model/AccessControlMode.kt",
    "content": "package yos.clash.material.service.model\n\nenum class AccessControlMode {\n    AcceptAll, AcceptSelected, DenySelected\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/model/Profile.kt",
    "content": "@file:UseSerializers(UUIDSerializer::class)\n\npackage yos.clash.material.service.model\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.github.kr328.clash.core.util.Parcelizer\nimport yos.clash.material.service.util.UUIDSerializer\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.UseSerializers\nimport java.util.*\n\n@Serializable\ndata class Profile(\n    val uuid: UUID,\n    val name: String,\n    val type: Type,\n    val source: String,\n    val active: Boolean,\n    val interval: Long,\n\n    val updatedAt: Long,\n    val imported: Boolean,\n    val pending: Boolean,\n\n    val moreInfo: MoreInfo?\n) : Parcelable {\n    enum class Type {\n        File, Url, External\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        Parcelizer.encodeToParcel(serializer(), parcel, this)\n    }\n\n    override fun describeContents(): Int {\n        return 0\n    }\n\n    companion object CREATOR : Parcelable.Creator<Profile> {\n        override fun createFromParcel(parcel: Parcel): Profile {\n            return Parcelizer.decodeFromParcel(serializer(), parcel)\n        }\n\n        override fun newArray(size: Int): Array<Profile?> {\n            return arrayOfNulls(size)\n        }\n    }\n}\n\n\n@Serializable\ndata class MoreInfo(\n    val uploadBase: Long,\n    val downloadBase: Long,\n    val totalBase: Long,\n    val expireTime: Long,\n)"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/remote/IClashManager.kt",
    "content": "package yos.clash.material.service.remote\n\nimport com.github.kr328.clash.core.model.UiConfiguration\nimport com.github.kr328.clash.core.Clash\nimport com.github.kr328.clash.core.model.ConfigurationOverride\nimport com.github.kr328.clash.core.model.Provider\nimport com.github.kr328.clash.core.model.ProviderList\nimport com.github.kr328.clash.core.model.ProxyGroup\nimport com.github.kr328.clash.core.model.ProxySort\nimport com.github.kr328.clash.core.model.TunnelState\nimport com.github.kr328.kaidl.BinderInterface\n\n@BinderInterface\ninterface IClashManager {\n    fun queryTunnelState(): TunnelState\n    fun queryTrafficTotal(): Long\n    fun queryProxyGroupNames(excludeNotSelectable: Boolean): List<String>\n    fun queryProxyGroup(name: String, proxySort: ProxySort): ProxyGroup\n    fun queryConfiguration(): UiConfiguration\n    fun queryProviders(): ProviderList\n\n    fun patchSelector(group: String, name: String): Boolean\n\n    suspend fun healthCheck(group: String)\n    suspend fun updateProvider(type: Provider.Type, name: String)\n\n    fun queryOverride(slot: Clash.OverrideSlot): ConfigurationOverride\n    fun patchOverride(slot: Clash.OverrideSlot, configuration: ConfigurationOverride)\n    fun clearOverride(slot: Clash.OverrideSlot)\n\n    fun setLogObserver(observer: ILogObserver?)\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/remote/IFetchObserver.kt",
    "content": "package yos.clash.material.service.remote\n\nimport com.github.kr328.clash.core.model.FetchStatus\nimport com.github.kr328.kaidl.BinderInterface\n\n@BinderInterface\nfun interface IFetchObserver {\n    fun updateStatus(status: FetchStatus)\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/remote/ILogObserver.kt",
    "content": "package yos.clash.material.service.remote\n\nimport com.github.kr328.clash.core.model.LogMessage\nimport com.github.kr328.kaidl.BinderInterface\n\n@BinderInterface\ninterface ILogObserver {\n    fun newItem(log: LogMessage)\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/remote/IProfileManager.kt",
    "content": "package yos.clash.material.service.remote\n\nimport yos.clash.material.service.model.Profile\nimport com.github.kr328.kaidl.BinderInterface\nimport java.util.*\n\n@BinderInterface\ninterface IProfileManager {\n    suspend fun create(type: Profile.Type, name: String, source: String = \"\"): UUID\n    suspend fun clone(uuid: UUID): UUID\n    suspend fun commit(uuid: UUID, callback: IFetchObserver? = null)\n    suspend fun release(uuid: UUID)\n    suspend fun delete(uuid: UUID)\n    suspend fun patch(uuid: UUID, name: String, source: String, interval: Long)\n    suspend fun update(uuid: UUID)\n    suspend fun queryByUUID(uuid: UUID): Profile?\n    suspend fun queryAll(): List<Profile>\n    suspend fun queryActive(): Profile?\n    suspend fun setActive(profile: Profile)\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/remote/IRemoteService.kt",
    "content": "package yos.clash.material.service.remote\n\nimport com.github.kr328.kaidl.BinderInterface\n\n@BinderInterface\ninterface IRemoteService {\n    fun clash(): IClashManager\n    fun profile(): IProfileManager\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/sideload/ExternalGeoip.kt",
    "content": "package yos.clash.material.service.sideload\n\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport yos.clash.material.common.constants.Metadata\nimport yos.clash.material.common.log.Log\nimport java.io.InputStream\n\nfun Context.readGeoipDatabaseFrom(packageName: String): ByteArray? {\n    return try {\n        val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)\n        val path = appInfo.metaData.getString(Metadata.GEOIP_FILE_NAME) ?: return null\n\n        createPackageContext(packageName, 0)\n            .resources.assets.open(path).use(InputStream::readBytes)\n    } catch (e: PackageManager.NameNotFoundException) {\n        Log.w(\"Sideload geoip: $packageName not found\", e)\n\n        null\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/store/ServiceStore.kt",
    "content": "package yos.clash.material.service.store\n\nimport android.content.Context\nimport yos.clash.material.common.store.Store\nimport yos.clash.material.common.store.asStoreProvider\nimport yos.clash.material.service.PreferenceProvider\nimport yos.clash.material.service.model.AccessControlMode\nimport java.util.*\n\nclass ServiceStore(context: Context) {\n    private val store = Store(\n        PreferenceProvider\n            .createSharedPreferencesFromContext(context)\n            .asStoreProvider()\n    )\n\n    var activeProfile: UUID? by store.typedString(\n        key = \"active_profile\",\n        from = { if (it.isBlank()) null else UUID.fromString(it) },\n        to = { it?.toString() ?: \"\" }\n    )\n\n    var bypassPrivateNetwork: Boolean by store.boolean(\n        key = \"bypass_private_network\",\n        defaultValue = true\n    )\n\n    var accessControlMode: AccessControlMode by store.enum(\n        key = \"access_control_mode\",\n        defaultValue = AccessControlMode.AcceptAll,\n        values = AccessControlMode.values()\n    )\n\n    var accessControlPackages by store.stringSet(\n        key = \"access_control_packages\",\n        defaultValue = emptySet()\n    )\n\n    var dnsHijacking by store.boolean(\n        key = \"dns_hijacking\",\n        defaultValue = true\n    )\n\n    var systemProxy by store.boolean(\n        key = \"system_proxy\",\n        defaultValue = true\n    )\n\n    var allowBypass by store.boolean(\n        key = \"allow_bypass\",\n        defaultValue = true\n    )\n\n    var dynamicNotification by store.boolean(\n        key = \"dynamic_notification\",\n        defaultValue = true\n    )\n\n    var sideloadGeoip by store.string(\n        key = \"sideload_geoip\",\n        defaultValue = \"\"\n    )\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Address.kt",
    "content": "package yos.clash.material.service.util\n\nimport java.net.Inet4Address\nimport java.net.Inet6Address\nimport java.net.InetAddress\n\nfun InetAddress.asSocketAddressText(port: Int): String {\n    return when (this) {\n        is Inet6Address ->\n            \"[${numericToTextFormat(this.address)}]:$port\"\n        is Inet4Address ->\n            \"${this.hostAddress}:$port\"\n        else -> throw IllegalArgumentException(\"Unsupported Inet type ${this.javaClass}\")\n    }\n}\n\nprivate const val INT16SZ = 2\nprivate const val INADDRSZ = 16\nprivate fun numericToTextFormat(src: ByteArray): String {\n    val sb = StringBuilder(39)\n    for (i in 0 until INADDRSZ / INT16SZ) {\n        sb.append(\n            Integer.toHexString(\n                src[i shl 1].toInt() shl 8 and 0xff00\n                        or (src[(i shl 1) + 1].toInt() and 0xff)\n            )\n        )\n        if (i < INADDRSZ / INT16SZ - 1) {\n            sb.append(\":\")\n        }\n    }\n    return sb.toString()\n}\n\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Broadcast.kt",
    "content": "package yos.clash.material.service.util\n\nimport android.content.Context\nimport android.content.Intent\nimport yos.clash.material.common.constants.Intents\nimport yos.clash.material.common.constants.Permissions\nimport java.util.*\n\nfun Context.sendBroadcastSelf(intent: Intent) {\n    sendBroadcast(\n        intent.setPackage(this.packageName),\n        Permissions.RECEIVE_SELF_BROADCASTS\n    )\n}\n\nfun Context.sendProfileChanged(uuid: UUID) {\n    val intent = Intent(Intents.ACTION_PROFILE_CHANGED)\n        .putExtra(Intents.EXTRA_UUID, uuid.toString())\n\n    sendBroadcastSelf(intent)\n}\n\nfun Context.sendProfileLoaded(uuid: UUID) {\n    val intent = Intent(Intents.ACTION_PROFILE_LOADED)\n        .putExtra(Intents.EXTRA_UUID, uuid.toString())\n\n    sendBroadcastSelf(intent)\n}\n\nfun Context.sendOverrideChanged() {\n    val intent = Intent(Intents.ACTION_OVERRIDE_CHANGED)\n\n    sendBroadcastSelf(intent)\n}\n\nfun Context.sendServiceRecreated() {\n    sendBroadcastSelf(Intent(Intents.ACTION_SERVICE_RECREATED))\n}\n\nfun Context.sendClashStarted() {\n    sendBroadcastSelf(Intent(Intents.ACTION_CLASH_STARTED))\n}\n\nfun Context.sendClashStopped(reason: String?) {\n    sendBroadcastSelf(\n        Intent(Intents.ACTION_CLASH_STOPPED).putExtra(\n            Intents.EXTRA_STOP_REASON,\n            reason\n        )\n    )\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Connectivity.kt",
    "content": "package yos.clash.material.service.util\n\nimport android.net.ConnectivityManager\nimport android.net.Network\n\nfun ConnectivityManager.resolvePrimaryDns(network: Network?): String? {\n    val properties = getLinkProperties(network) ?: return null\n\n    return properties.dnsServers.firstOrNull()?.asSocketAddressText(53)\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Coroutine.kt",
    "content": "package yos.clash.material.service.util\n\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.job\nimport kotlinx.coroutines.runBlocking\n\nfun CoroutineScope.cancelAndJoinBlocking() {\n    val scope = this\n\n    runBlocking {\n        scope.coroutineContext.job.cancel()\n        scope.coroutineContext.job.join()\n    }\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Database.kt",
    "content": "package yos.clash.material.service.util\n\nimport yos.clash.material.service.data.ImportedDao\nimport yos.clash.material.service.data.PendingDao\nimport java.util.*\n\nsuspend fun generateProfileUUID(): UUID {\n    var result = UUID.randomUUID()\n\n    while (ImportedDao().exists(result) || PendingDao().exists(result)) {\n        result = UUID.randomUUID()\n    }\n\n    return result\n}\n"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Files.kt",
    "content": "package yos.clash.material.service.util\n\nimport android.content.Context\nimport java.io.File\n\nval Context.importedDir: File\n    get() = filesDir.resolve(\"imported\")\n\nval Context.pendingDir: File\n    get() = filesDir.resolve(\"pending\")\n\nval Context.processingDir: File\n    get() = filesDir.resolve(\"processing\")\n\nval File.directoryLastModified: Long?\n    get() {\n        return walk().map { it.lastModified() }.maxOrNull()\n    }"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Intent.kt",
    "content": "package yos.clash.material.service.util\n\nimport android.content.Intent\n\nval Intent.packageName: String?\n    get() {\n        return data?.takeIf { it.scheme == \"package\" }?.schemeSpecificPart\n    }"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Net.kt",
    "content": "package yos.clash.material.service.util\n\ndata class IPNet(val ip: String, val prefix: Int)\n\nfun parseCIDR(cidr: String): IPNet {\n    val s = cidr.split(\"/\", limit = 2)\n\n    if (s.size != 2)\n        throw IllegalArgumentException(\"Invalid address\")\n\n    val address = s[0]\n    val prefix = s[1].toInt()\n\n    return IPNet(address, prefix)\n}"
  },
  {
    "path": "service/src/main/java/yos/clash/material/service/util/Serializers.kt",
    "content": "package yos.clash.material.service.util\n\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.descriptors.PrimitiveKind\nimport kotlinx.serialization.descriptors.PrimitiveSerialDescriptor\nimport kotlinx.serialization.descriptors.SerialDescriptor\nimport kotlinx.serialization.encoding.Decoder\nimport kotlinx.serialization.encoding.Encoder\nimport java.util.*\n\nclass UUIDSerializer : KSerializer<UUID> {\n    override val descriptor: SerialDescriptor =\n        PrimitiveSerialDescriptor(\"UUID\", PrimitiveKind.STRING)\n\n    override fun deserialize(decoder: Decoder): UUID {\n        return UUID.fromString(decoder.decodeString())\n    }\n\n    override fun serialize(encoder: Encoder, value: UUID) {\n        encoder.encodeString(value.toString())\n    }\n}"
  },
  {
    "path": "service/src/main/res/drawable/ic_logo_service.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:width=\"200dp\"\n    android:height=\"200dp\"\n    android:viewportWidth=\"200\"\n    android:viewportHeight=\"200\"\n    android:tint=\"@color/color_clash\">\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M40,163.1C62.1-28,59.3,17.9,84.3,46.2c12.7-1.4,25.6-3.3,38.4-0.7c2.4,0.1,5.1,1.8,7.2,0.2\n\tc3.9-5.9,7-12.7,10.7-18.8c11.5-22.9,12,18.3,14.6,24.9c4.9,31,10.3,60.7,13.8,91.8c0.3,10.1,5.4,30.9,1,31.6\n\tc-46.5,4.9-138.9,3.7-151.2-5.9c-14.9-9.9-14.3-33.2,1.9-41.1c3.4-1.9,10.1-3.5,11.2,0.7c-2.3,4.7-8.3,5.5-11.4,10.5\n\tC11.4,154.4,27,163.8,40.2,163.1z M78.4,74.6c-9.8,0.5-9,14-0.1,14.3C88.2,88.4,87.4,74.8,78.4,74.6z M136.1,88.9\n\tc9.9-0.3,9.2-13.8,0.4-14.3C126.6,74.8,127.2,88.4,136.1,88.9z M97.3,98.3c2.1,3.3,5,3.1,9.2,0.2c3.6,3,7.6,3.1,8.7-0.2\n\tc-3.6,1.1-6.6,0.8-8.8-3.3C104.4,99.1,101.3,99.4,97.3,98.3z\"\n        tools:ignore=\"VectorPath\" />\n</vector>\n"
  },
  {
    "path": "service/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- exclude 127.0.0.0/8 169.254.0.0/16 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 -->\n    <string-array name=\"bypass_private_route\" translatable=\"false\">\n        <item>1.0.0.0/8</item>\n        <item>2.0.0.0/7</item>\n        <item>4.0.0.0/6</item>\n        <item>8.0.0.0/7</item>\n        <item>11.0.0.0/8</item>\n        <item>12.0.0.0/6</item>\n        <item>16.0.0.0/4</item>\n        <item>32.0.0.0/3</item>\n        <item>64.0.0.0/3</item>\n        <item>96.0.0.0/4</item>\n        <item>112.0.0.0/5</item>\n        <item>120.0.0.0/6</item>\n        <item>124.0.0.0/7</item>\n        <item>126.0.0.0/8</item>\n        <item>128.0.0.0/3</item>\n        <item>160.0.0.0/5</item>\n        <item>168.0.0.0/8</item>\n        <item>169.0.0.0/9</item>\n        <item>169.128.0.0/10</item>\n        <item>169.192.0.0/11</item>\n        <item>169.224.0.0/12</item>\n        <item>169.240.0.0/13</item>\n        <item>169.248.0.0/14</item>\n        <item>169.252.0.0/15</item>\n        <item>169.255.0.0/16</item>\n        <item>170.0.0.0/7</item>\n        <item>172.0.0.0/12</item>\n        <item>172.32.0.0/11</item>\n        <item>172.64.0.0/10</item>\n        <item>172.128.0.0/9</item>\n        <item>173.0.0.0/8</item>\n        <item>174.0.0.0/7</item>\n        <item>176.0.0.0/4</item>\n        <item>192.0.0.0/9</item>\n        <item>192.128.0.0/11</item>\n        <item>192.160.0.0/13</item>\n        <item>192.169.0.0/16</item>\n        <item>192.170.0.0/15</item>\n        <item>192.172.0.0/14</item>\n        <item>192.176.0.0/12</item>\n        <item>192.192.0.0/10</item>\n        <item>193.0.0.0/8</item>\n        <item>194.0.0.0/7</item>\n        <item>196.0.0.0/6</item>\n        <item>200.0.0.0/5</item>\n        <item>208.0.0.0/4</item>\n        <item>240.0.0.0/5</item>\n        <item>248.0.0.0/6</item>\n        <item>252.0.0.0/7</item>\n        <item>254.0.0.0/8</item>\n        <item>255.0.0.0/9</item>\n        <item>255.128.0.0/10</item>\n        <item>255.192.0.0/11</item>\n        <item>255.224.0.0/12</item>\n        <item>255.240.0.0/13</item>\n        <item>255.248.0.0/14</item>\n        <item>255.252.0.0/15</item>\n        <item>255.254.0.0/16</item>\n        <item>255.255.0.0/17</item>\n        <item>255.255.128.0/18</item>\n        <item>255.255.192.0/19</item>\n        <item>255.255.224.0/20</item>\n        <item>255.255.240.0/21</item>\n        <item>255.255.248.0/22</item>\n        <item>255.255.252.0/23</item>\n        <item>255.255.254.0/24</item>\n        <item>255.255.255.0/25</item>\n        <item>255.255.255.128/26</item>\n        <item>255.255.255.192/27</item>\n        <item>255.255.255.224/28</item>\n        <item>255.255.255.240/29</item>\n        <item>255.255.255.248/30</item>\n        <item>255.255.255.252/31</item>\n        <item>255.255.255.254/32</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "service/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"color_clash\">#1E4376</color>\n</resources>"
  },
  {
    "path": "service/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"nf_clash_status\" type=\"id\" />\n    <item name=\"nf_vpn_status\" type=\"id\" />\n    <item name=\"nf_profile_worker\" type=\"id\" />\n</resources>"
  },
  {
    "path": "service/src/main/res/values/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"PluralsCandidate\">\n    <!-- from https://github.com/shadowsocks/shadowsocks-android/blob/master/core/src/main/res/values/strings.xml -->\n    <string name=\"clash_notification_content\" translatable=\"false\">\"%1$s↑\\t%2$s↓\"</string>\n\n    <string name=\"clash_service_status_channel\">Clash You Status</string>\n    <string name=\"profile_service_status\">Profile Service Status</string>\n    <string name=\"profile_process_status\">Profile Processing Status</string>\n    <string name=\"profile_process_result\">Profile Process Result</string>\n    <string name=\"update_successfully\">Update Successfully</string>\n    <string name=\"update_failure\">Update Failure</string>\n    <string name=\"format_update_complete\">Update %s completed</string>\n    <string name=\"format_update_failure\">Update %1$s: %2$s</string>\n    <string name=\"running\">Running</string>\n    <string name=\"loading\">Loading</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"profiles_and_providers\">Profiles and Providers</string>\n    <string name=\"configuration_yaml\">Configuration.yaml</string>\n    <string name=\"provider_files\">Provider Files</string>\n    <string name=\"profile_updater\">Profile Updater</string>\n    <string name=\"profile_updating\">Profile Updating</string>\n</resources>\n"
  },
  {
    "path": "service/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"clash_service_status_channel\">Clash You 状态</string>\n    <string name=\"running\">正在运行</string>\n    <string name=\"format_update_complete\">更新 %s 成功</string>\n    <string name=\"format_update_failure\">\"更新 %1$s: %2$s \"</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"profiles_and_providers\">配置文件和外部资源</string>\n    <string name=\"configuration_yaml\">配置文件.yaml</string>\n    <string name=\"provider_files\">外部资源文件列表</string>\n    <string name=\"loading\">载入中</string>\n    <string name=\"profile_process_status\">配置文件处理状态</string>\n    <string name=\"update_successfully\">更新成功</string>\n    <string name=\"update_failure\">更新失败</string>\n    <string name=\"profile_updater\">配置更新服务</string>\n    <string name=\"profile_updating\">配置更新中</string>\n    <string name=\"profile_service_status\">配置文件服务状态</string>\n    <string name=\"profile_process_result\">配置文件处理结果</string>\n</resources>"
  },
  {
    "path": "service/src/main/res/values-zh-rHK/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"clash_service_status_channel\">Clash You 狀態</string>\n    <string name=\"running\">正在運行</string>\n    <string name=\"format_update_complete\">更新 %s 成功</string>\n    <string name=\"format_update_failure\">\"更新 %1$s: %2$s \"</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"profiles_and_providers\">配置文件和外部資源</string>\n    <string name=\"configuration_yaml\">配置文件.yaml</string>\n    <string name=\"provider_files\">外部資源文件列表</string>\n    <string name=\"loading\">載入中</string>\n    <string name=\"profile_process_status\">配置文件處理狀態</string>\n    <string name=\"update_successfully\">更新成功</string>\n    <string name=\"update_failure\">更新失敗</string>\n    <string name=\"profile_updater\">配置更新服務</string>\n    <string name=\"profile_updating\">配置更新中</string>\n    <string name=\"profile_service_status\">配置文件服務狀態</string>\n    <string name=\"profile_process_result\">配置文件處理結果</string>\n</resources>"
  },
  {
    "path": "service/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"clash_service_status_channel\">Clash You 狀態</string>\n    <string name=\"running\">正在運作</string>\n    <string name=\"format_update_complete\">更新 %s 成功</string>\n    <string name=\"format_update_failure\">\"更新 %1$s: %2$s \"</string>\n    <string name=\"clash_for_android\">Clash You</string>\n    <string name=\"profiles_and_providers\">設定檔和外部資源</string>\n    <string name=\"configuration_yaml\">設定檔.yaml</string>\n    <string name=\"provider_files\">外部資源文件列表</string>\n    <string name=\"loading\">載入中</string>\n    <string name=\"profile_process_status\">設定檔處理狀態</string>\n    <string name=\"update_successfully\">更新成功</string>\n    <string name=\"update_failure\">更新失敗</string>\n    <string name=\"profile_updater\">設定檔更新服務</string>\n    <string name=\"profile_updating\">設定檔更新中</string>\n    <string name=\"profile_service_status\">設定檔服務狀態</string>\n    <string name=\"profile_process_result\">設定檔處理結果</string>\n</resources>\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "rootProject.name = \"ClashYou\"\n\ninclude(\":app\")\ninclude(\":core\")\ninclude(\":service\")\ninclude(\":design\")\ninclude(\":common\")\ninclude(\":hideapi\")\n\npluginManagement {\n    repositories {\n        mavenLocal()\n        mavenCentral()\n        gradlePluginPortal()\n        google()\n        maven(\"https://jitpack.io\")\n        maven(\"https://oss.sonatype.org/content/repositories/snapshots/\")\n    }\n}\n\ndependencyResolutionManagement {\n    versionCatalogs {\n        create(\"libs\") {\n            val agp = \"7.2.1\"\n            val kotlin = \"1.7.0\"\n            val ksp = \"$kotlin-1.0.6\"\n            val golang = \"1.0.4\"\n            val coroutine = \"1.6.3\"\n            val coreKtx = \"1.8.0\"\n            val activity = \"1.5.0\"\n            val fragment = \"1.5.0\"\n            val appcompat = \"1.4.2\"\n            val coordinator = \"1.2.0\"\n            val recyclerview = \"1.2.1\"\n            val viewpager = \"1.0.0\"\n            val material = \"1.9.0\"\n            val serialization = \"1.3.3\"\n            val kaidl = \"1.15\"\n            val room = \"2.4.2\"\n            val multiprocess = \"1.0.0\"\n            val splashscreen = \"1.0.1\"\n            val xxpermission = \"18.2\"\n\n            library(\"build-android\", \"com.android.tools.build:gradle:$agp\")\n            library(\"build-kotlin-common\", \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin\")\n            library(\n                \"build-kotlin-serialization\",\n                \"org.jetbrains.kotlin:kotlin-serialization:$kotlin\"\n            )\n            library(\"build-ksp\", \"com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp\")\n            library(\"build-golang\", \"com.github.kr328.golang:gradle-plugin:$golang\")\n            library(\n                \"kotlin-coroutine\",\n                \"org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine\"\n            )\n            library(\n                \"kotlin-serialization-json\",\n                \"org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization\"\n            )\n            library(\"androidx-core\", \"androidx.core:core-ktx:$coreKtx\")\n            library(\"androidx-activity\", \"androidx.activity:activity:$activity\")\n            library(\"androidx-fragment\", \"androidx.fragment:fragment:$fragment\")\n            library(\"androidx-appcompat\", \"androidx.appcompat:appcompat:$appcompat\")\n            library(\n                \"androidx-coordinator\",\n                \"androidx.coordinatorlayout:coordinatorlayout:$coordinator\"\n            )\n            library(\"androidx-recyclerview\", \"androidx.recyclerview:recyclerview:$recyclerview\")\n            library(\"androidx-viewpager\", \"androidx.viewpager2:viewpager2:$viewpager\")\n            library(\"androidx-room-compiler\", \"androidx.room:room-compiler:$room\")\n            library(\"androidx-room-runtime\", \"androidx.room:room-runtime:$room\")\n            library(\"androidx-room-ktx\", \"androidx.room:room-ktx:$room\")\n            library(\"google-material\", \"com.google.android.material:material:$material\")\n            library(\"kaidl-compiler\", \"com.github.kr328.kaidl:kaidl:$kaidl\")\n            library(\"kaidl-runtime\", \"com.github.kr328.kaidl:kaidl-runtime:$kaidl\")\n            library(\"rikkax-multiprocess\", \"dev.rikka.rikkax.preference:multiprocess:$multiprocess\")\n\n            library(\"androidx-splashscreen\", \"androidx.core:core-splashscreen:$splashscreen\")\n            //权限请求框架\n            library(\n                \"getactivity-xxpermission\",\n                \"com.github.getActivity:XXPermissions:$xxpermission\"\n            )\n        }\n    }\n}"
  }
]