[
  {
    "path": ".gitattributes",
    "content": "# Set the default behavior, in case people don't have core.autocrlf set.\n* text=auto eol=lf\n\n# Declare files that will always have CRLF line endings on checkout.\n*.cmd text eol=crlf\n*.bat text eol=crlf\n\n# Denote all files that are truly binary and should not be modified.\n*.so binary\n*.dex binary\n*.jar binary\n*.png binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report/反馈 Bug\ndescription: Report errors or unexpected behavior./反馈错误或异常行为。\nlabels: [bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for reporting issues of LSPosed!\n\n        To make it easier for us to help you, please read all pinned issues and provide the following details.\n\n        感谢给 LSPosed 汇报问题！\n        为了使我们更好地帮助你，请务必阅读所有已置顶的 Issues 并提供以下细节。\n        为了防止重复汇报，标题请务必使用英文。\n  - type: textarea\n    attributes:\n      label: Steps to reproduce/复现步骤\n      placeholder: |\n        1.\n        2.\n        3.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Expected behaviour/预期行为\n      placeholder: Tell us what should happen/正常情况下应该发生什么\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Actual behaviour/实际行为\n      placeholder: Tell us what happens instead/实际上发生了什么\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Xposed Module List/Xposed 模块列表\n      render: shell\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Root implementation/Root 方案\n      description: Common root implementations include Magisk, KernelSU or APatch. Please specify your current implementation with exact version./常见的 Root 方案有 Magisk，KernelSU 以及 APatch。请注明您当前的方案以及详细版本。\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: System Module List/系统模块列表\n      description: Modules installed through your root implementation manager/通过您 Root 方案的管理器所安装的模块\n      render: shell\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: LSPosed version/LSPosed 版本\n      description: Don't use 'latest'. Specify actual version with 4 digits, otherwise your issue will be closed./不要填用“最新版”。给出四位版本号，不然 issue 会被关闭。\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Android version/Android 版本\n      description: If you are running a custom OS, please also note it./如果使用了非官方的操作系统，请一并注明。\n    validations:\n      required: true\n  - type: checkboxes\n    id: latest\n    attributes:\n      label: Version requirement/版本要求\n      options:\n        - label: I am using the latest debug build from [GitHub Actions](https://github.com/JingMatrix/LSPosed/actions?query=branch%3Amaster)./我正在使用 [GitHub Actions](https://github.com/JingMatrix/LSPosed/actions?query=branch%3Amaster) 中最新的调试版本。\n          required: true\n  - type: textarea\n    attributes:\n      label: Logs/日志\n      description: For usage issues, please provide the log zip saved from manager; for activation issues, please provide [bugreport](https://developer.android.com/studio/debug/bug-report). Without logs zip, the issue will be closed. /使用问题请提供从管理器保存的日志压缩包；激活问题请提供 [bugreport](https://developer.android.google.cn/studio/debug/bug-report?hl=zh-cn) 日志。没有日志附件的问题会被关闭。\n      placeholder: Upload logs zip by clicking the bar on the bottom. Uploading logs to other websites or using external links is prohibited. /点击文本框底栏上传日志压缩包，禁止上传到其它网站或使用外链提供日志。\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question/提问\n    url: https://github.com/JingMatrix/LSPosed/discussions/new?category=Q-A\n    about: Please ask and answer questions here./如果有任何疑问请在这里提问\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "---\nname: Feature request/新特性请求\ndescription: Suggest an idea./提出建议\nlabels: [enhancement]\nbody:\n  - type: textarea\n    attributes:\n      label: Is your feature request related to a problem?/你的请求是否与某个问题相关？\n      placeholder: A clear and concise description of what the problem is./请清晰准确表述该问题。\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe the solution you'd like/描述你想要的解决方案\n      placeholder: A clear and concise description of what you want to happen./请清晰准确描述新特性的预期行为\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Additional context/其他信息\n      placeholder: Add any other context or screenshots about the feature request here./其他关于新特性的信息或者截图\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: monthly\n    groups:\n      actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: gitsubmodule\n    directory: /\n    schedule:\n      interval: monthly\n    groups:\n      submodule:\n        patterns:\n          - \"*\"\n  - package-ecosystem: gradle\n    directory: /\n    schedule:\n      interval: daily\n    groups:\n      maven:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/workflows/core.yml",
    "content": "name: Core\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ master ]\n    tags: [ v* ]\n  pull_request:\n  merge_group:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    env:\n      CCACHE_COMPILERCHECK: \"%compiler% -dumpmachine; %compiler% -dumpversion\"\n      CCACHE_NOHASHDIR: \"true\"\n      CCACHE_HARDLINK: \"true\"\n      CCACHE_BASEDIR: \"${{ github.workspace }}\"\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Write key\n        if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/master' ) || github.ref_type == 'tag' }}\n        run: |\n          if [ ! -z \"${{ secrets.KEY_STORE }}\" ]; then\n            echo androidStorePassword='${{ secrets.KEY_STORE_PASSWORD }}' >> gradle.properties\n            echo androidKeyAlias='${{ secrets.ALIAS }}' >> gradle.properties\n            echo androidKeyPassword='${{ secrets.KEY_PASSWORD }}' >> gradle.properties\n            echo androidStoreFile='key.jks' >> gradle.properties\n            echo ${{ secrets.KEY_STORE }} | base64 --decode > key.jks\n          fi\n\n      - name: Setup Java\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: 21\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v5\n\n      - name: Configure Gradle properties\n        run: |\n          echo 'android.native.buildOutput=verbose' >> ~/.gradle/gradle.properties\n          echo 'org.gradle.parallel=true' >> ~/.gradle/gradle.properties\n          echo 'org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -XX:+UseParallelGC' >> ~/.gradle/gradle.properties\n          echo 'android.native.buildOutput=verbose' >> ~/.gradle/gradle.properties\n\n      - name: Setup ninja\n        uses: seanmiddleditch/gha-setup-ninja@v6\n        with:\n          version: 1.12.1\n\n      - name: Setup ccache\n        uses: actions/cache@v5\n        with:\n          path: |\n            ~/.ccache\n            ${{ github.workspace }}/.ccache\n          key: ${{ runner.os }}-ccache-${{ hashFiles('**/build.gradle') }}-${{ hashFiles('**/CMakeLists.txt') }}\n          restore-keys: |\n            ${{ runner.os }}-ccache-\n\n      - name: Setup Android SDK\n        uses: android-actions/setup-android@v3\n\n      - name: Remove Android's cmake\n        shell: bash\n        run: rm -rf $ANDROID_HOME/cmake\n\n      - name: Build with Gradle\n        run: |\n          ./gradlew zipAll\n\n      - name: Prepare artifact\n        if: success()\n        id: prepareArtifact\n        run: |\n          zygiskReleaseName=`ls zygisk/release/Vector-v*-Release.zip | awk -F '(/|.zip)' '{print $3}'` && echo \"zygiskReleaseName=$zygiskReleaseName\" >> $GITHUB_OUTPUT\n          zygiskDebugName=`ls zygisk/release/Vector-v*-Debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo \"zygiskDebugName=$zygiskDebugName\" >> $GITHUB_OUTPUT\n          unzip zygisk/release/Vector-v*-Release.zip -d Vector-Release\n          unzip zygisk/release/Vector-v*-Debug.zip -d Vector-Debug\n\n      - name: Upload zygisk release\n        uses: actions/upload-artifact@v6\n        with:\n          name: ${{ steps.prepareArtifact.outputs.zygiskReleaseName }}\n          path: \"./Vector-Release/*\"\n\n      - name: Upload zygisk debug\n        uses: actions/upload-artifact@v6\n        with:\n          name: ${{ steps.prepareArtifact.outputs.zygiskDebugName }}\n          path: \"./Vector-Debug/*\"\n\n      - name: Upload mappings\n        uses: actions/upload-artifact@v6\n        with:\n          name: mappings\n          path: |\n            zygisk/build/outputs/mapping\n            app/build/outputs/mapping\n\n      - name: Upload symbols\n        uses: actions/upload-artifact@v6\n        with:\n          name: symbols\n          path: build/symbols\n\n"
  },
  {
    "path": ".github/workflows/crowdin.yml",
    "content": "name: Crowdin Action\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ master ]\n    paths:\n      - app/src/main/res/values/strings.xml\n      - daemon/src/main/res/values/strings.xml\n\njobs:\n  synchronize-with-crowdin:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout\n      uses: actions/checkout@main\n\n    - name: crowdin action\n      uses: crowdin/github-action@master\n      with:\n        upload_translations: true\n        download_translations: false\n        upload_sources: true\n        config: 'crowdin.yml'\n        crowdin_branch_name: master\n      env:\n        CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}\n        CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".project\n.settings\n.cache\n*.iml\n.gradle\n/local.properties\n/.idea\n.DS_Store\nbuild\n/captures\nbin/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"external/lsplant\"]\n\tpath = external/lsplant\n\turl = https://github.com/JingMatrix/LSPlant.git\n[submodule \"external/dobby\"]\n\tpath = external/dobby\n\turl = https://github.com/JingMatrix/Dobby.git\n[submodule \"external/fmt\"]\n\tpath = external/fmt\n\turl = https://github.com/fmtlib/fmt.git\n[submodule \"external/xz-embedded\"]\n\tpath = external/xz-embedded\n\turl = https://github.com/tukaani-project/xz-embedded.git\n[submodule \"external/lsplt\"]\n\tpath = external/lsplt\n\turl = https://github.com/JingMatrix/LSPlt\n[submodule \"services/libxposed\"]\n\tpath = services/libxposed\n\turl = https://github.com/libxposed/service.git\n[submodule \"xposed/libxposed\"]\n\tpath = xposed/libxposed\n\turl = https://github.com/libxposed/api.git\n[submodule \"external/apache/commons-lang\"]\n\tpath = external/apache/commons-lang\n\turl = https://github.com/apache/commons-lang.git\n[submodule \"external/axml/manifest-editor\"]\n\tpath = external/axml/manifest-editor\n\turl = https://github.com/JingMatrix/ManifestEditor.git\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": "README.md",
    "content": "# LSPosed Framework\n\n[![Build](https://img.shields.io/github/actions/workflow/status/JingMatrix/LSPosed/core.yml?branch=master&event=push&logo=github&label=Build)](https://github.com/JingMatrix/LSPosed/actions/workflows/core.yml?query=event%3Apush+branch%3Amaster+is%3Acompleted) [![Crowdin](https://img.shields.io/badge/Localization-Crowdin-blueviolet?logo=Crowdin)](https://crowdin.com/project/lsposed_jingmatrix) [![Download](https://img.shields.io/github/v/release/JingMatrix/LSPosed?color=orange&logoColor=orange&label=Download&logo=DocuSign)](https://github.com/JingMatrix/LSPosed/releases/latest) [![Total](https://shields.io/github/downloads/JingMatrix/LSPosed/total?logo=Bookmeter&label=Counts&logoColor=yellow&color=yellow)](https://github.com/JingMatrix/LSPosed/releases)\n\n## Introduction \n\nA Zygisk module trying to provide an ART hooking framework which delivers consistent APIs with the OG Xposed, leveraging LSPlant hooking framework.\n\n> Xposed is a framework for modules that can change the behavior of the system and apps without touching any APKs. That's great because it means that modules can work for different versions and even ROMs without any changes (as long as the original code was not changed too much). It's also easy to undo. As all changes are done in the memory, you just need to deactivate the module and reboot to get your original system back. There are many other advantages, but here is just one more: multiple modules can do changes to the same part of the system or app. With modified APKs, you have to choose one. No way to combine them, unless the author builds multiple APKs with different combinations.\n\n## Supported Versions\n\nAndroid 8.1 ~ 16\n\n## Install\n\n1. Install Magisk v26+\n2. [Download](#download) and install LSPosed in Magisk app\n3. Reboot\n4. Open LSPosed manager from notification\n5. Have fun :)\n\n## Download\n\n- For stable releases, please go to [Github Releases page](https://github.com/JingMatrix/LSPosed/releases)\n- For canary build, please check [Github Actions](https://github.com/JingMatrix/LSPosed/actions/workflows/core.yml?query=branch%3Amaster)\n\nNote: debug builds are only available in Github Actions.\n\n## Get Help\n**Only bug reports from **THE LATEST DEBUG BUILD** will be accepted.**\n- GitHub issues: [Issues](https://github.com/JingMatrix/LSPosed/issues/)\n- (For Chinese speakers) 本项目只接受英语**标题**的issue。如果您不懂英语，请使用[翻译工具](https://www.deepl.com/zh/translator)\n\n## For Developers\n\nDevelopers are welcome to write Xposed modules with hooks based on LSPosed Framework. A module based on LSPosed framework is fully compatible with the original Xposed Framework, and vice versa, a Xposed Framework-based module will work well with LSPosed framework too.\n\n- [Xposed Framework API](https://api.xposed.info/)\n\nWe use our own module repository. We welcome developers to submit modules to our repository, and then modules can be downloaded in LSPosed.\n\n- [LSPosed Module Repository](https://github.com/Xposed-Modules-Repo)\n\n## Community Discussion\n\n[Troubleshooting guide](https://github.com/JingMatrix/LSPosed/issues/123) and [Disscusions](https://github.com/JingMatrix/LSPosed/discussions).\n\n## Translation Contributing\n\nYou can contribute translation [here](https://crowdin.com/project/lsposed_jingmatrix).\n\n## Credits \n\n- [Magisk](https://github.com/topjohnwu/Magisk/): makes all these possible\n- [XposedBridge](https://github.com/rovo89/XposedBridge): the OG Xposed framework APIs\n- [LSPlant](https://github.com/JingMatrix/LSPlant): the core ART hooking framework\n- [Dobby](https://github.com/JingMatrix/Dobby): inline hooker for `LSPlant` and `native_api` implement\n- [EdXposed](https://github.com/ElderDrivers/EdXposed): fork source\n- [xz-embedded](https://github.com/tukaani-project/xz-embedded): decompress `.gnu_debugdata` header section of stripped `libart.so`\n- ~~[Riru](https://github.com/RikkaApps/Riru): provides a way to inject code into zygote process~~\n- ~[SandHook](https://github.com/ganyao114/SandHook/): ART hooking framework for SandHook variant~\n- ~[YAHFA](https://github.com/rk700/YAHFA): previous ART hooking framework~\n- ~[dexmaker](https://github.com/linkedin/dexmaker) and [dalvikdx](https://github.com/JakeWharton/dalvik-dx): to dynamically generate YAHFA hooker classes~\n- ~[DexBuilder](https://github.com/LSPosed/DexBuilder): to dynamically generate YAHFA hooker classes~\n\n## License\n\nLSPosed is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html).\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle.kts",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\nimport java.time.Instant\n\nplugins {\n    alias(libs.plugins.agp.app)\n    alias(libs.plugins.nav.safeargs)\n    alias(libs.plugins.autoresconfig)\n    alias(libs.plugins.materialthemebuilder)\n    alias(libs.plugins.lsplugin.resopt)\n    alias(libs.plugins.lsplugin.apksign)\n}\n\napksign {\n    storeFileProperty = \"androidStoreFile\"\n    storePasswordProperty = \"androidStorePassword\"\n    keyAliasProperty = \"androidKeyAlias\"\n    keyPasswordProperty = \"androidKeyPassword\"\n}\n\nval defaultManagerPackageName: String by rootProject.extra\n\nandroid {\n    buildFeatures {\n        viewBinding = true\n        buildConfig = true\n    }\n\n    defaultConfig {\n        applicationId = defaultManagerPackageName\n        buildConfigField(\"long\", \"BUILD_TIME\", Instant.now().epochSecond.toString())\n    }\n\n    packaging {\n        resources {\n            excludes += \"META-INF/**\"\n            excludes += \"okhttp3/**\"\n            excludes += \"kotlin/**\"\n            excludes += \"org/**\"\n            excludes += \"**.properties\"\n            excludes += \"**.bin\"\n        }\n    }\n\n    dependenciesInfo.includeInApk = false\n\n    buildTypes {\n        release {\n            isMinifyEnabled = true\n            isShrinkResources = true\n            proguardFiles(\"proguard-rules.pro\")\n        }\n    }\n\n    sourceSets { named(\"main\") { res { srcDirs(\"src/common/res\") } } }\n    namespace = defaultManagerPackageName\n}\n\nautoResConfig {\n    generateClass = true\n    generateRes = false\n    generatedClassFullName = \"org.lsposed.manager.util.LangList\"\n    generatedArrayFirstItem = \"SYSTEM\"\n}\n\nmaterialThemeBuilder {\n    themes {\n        for ((name, color) in\n            listOf(\n                \"Red\" to \"F44336\",\n                \"Pink\" to \"E91E63\",\n                \"Purple\" to \"9C27B0\",\n                \"DeepPurple\" to \"673AB7\",\n                \"Indigo\" to \"3F51B5\",\n                \"Blue\" to \"2196F3\",\n                \"LightBlue\" to \"03A9F4\",\n                \"Cyan\" to \"00BCD4\",\n                \"Teal\" to \"009688\",\n                \"Green\" to \"4FAF50\",\n                \"LightGreen\" to \"8BC3A4\",\n                \"Lime\" to \"CDDC39\",\n                \"Yellow\" to \"FFEB3B\",\n                \"Amber\" to \"FFC107\",\n                \"Orange\" to \"FF9800\",\n                \"DeepOrange\" to \"FF5722\",\n                \"Brown\" to \"795548\",\n                \"BlueGrey\" to \"607D8F\",\n                \"Sakura\" to \"FF9CA8\",\n            )) {\n            create(\"Material$name\") {\n                lightThemeFormat = \"ThemeOverlay.Light.%s\"\n                darkThemeFormat = \"ThemeOverlay.Dark.%s\"\n                primaryColor = \"#$color\"\n            }\n        }\n    }\n    // Add Material Design 3 color tokens (such as palettePrimary100) in generated theme\n    // rikka.material:material >= 2.0.0 provides such attributes\n    // Enable this if your are using rikka.material:material\n    generatePalette = true\n}\n\ndependencies {\n    annotationProcessor(libs.glide.compiler)\n    implementation(libs.androidx.activity)\n    implementation(libs.androidx.browser)\n    implementation(libs.androidx.constraintlayout)\n    implementation(libs.androidx.core)\n    implementation(libs.androidx.fragment)\n    implementation(libs.androidx.navigation.fragment)\n    implementation(libs.androidx.navigation.ui)\n    implementation(libs.androidx.preference)\n    implementation(libs.androidx.recyclerview)\n    implementation(libs.androidx.swiperefreshlayout)\n    implementation(libs.glide)\n    implementation(libs.material)\n    implementation(libs.gson)\n    implementation(libs.okhttp)\n    implementation(libs.okhttp.dnsoverhttps)\n    implementation(libs.okhttp.logging.interceptor)\n    implementation(libs.rikkax.appcompat)\n    implementation(libs.rikkax.core)\n    implementation(libs.rikkax.insets)\n    implementation(libs.rikkax.material)\n    implementation(libs.rikkax.material.preference)\n    implementation(libs.rikkax.recyclerview)\n    implementation(libs.rikkax.widget.borderview)\n    implementation(libs.rikkax.widget.mainswitchbar)\n    implementation(libs.rikkax.layoutinflater)\n    implementation(libs.appiconloader)\n    implementation(libs.hiddenapibypass)\n    implementation(libs.kotlin.stdlib)\n    implementation(libs.kotlinx.coroutines.core)\n    implementation(projects.services.managerService)\n}\n\nconfigurations.all {\n    exclude(\"org.jetbrains\", \"annotations\")\n    exclude(\"androidx.appcompat\", \"appcompat\")\n    exclude(\"org.jetbrains.kotlin\", \"kotlin-stdlib-jdk7\")\n    exclude(\"org.jetbrains.kotlin\", \"kotlin-stdlib-jdk8\")\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "-keep class org.lsposed.manager.Constants {\n    public static boolean setBinder(android.os.IBinder);\n}\n-assumenosideeffects class kotlin.jvm.internal.Intrinsics {\n    public static void check*(...);\n    public static void throw*(...);\n}\n-assumenosideeffects class android.util.Log {\n    public static *** v(...);\n    public static *** d(...);\n}\n\n-keepclasseswithmembers,allowobfuscation class * {\n  @com.google.gson.annotations.SerializedName <fields>;\n}\n\n-repackageclasses\n-allowaccessmodification\n-overloadaggressively\n\n# Gson uses generic type information stored in a class file when working with fields. Proguard\n# removes such information by default, so configure it to keep all of it.\n-keepattributes Signature,InnerClasses,EnclosingMethod\n\n-dontwarn org.jetbrains.annotations.NotNull\n-dontwarn org.jetbrains.annotations.Nullable\n-dontwarn org.bouncycastle.jsse.BCSSLParameters\n-dontwarn org.bouncycastle.jsse.BCSSLSocket\n-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider\n-dontwarn org.conscrypt.Conscrypt*\n-dontwarn org.conscrypt.ConscryptHostnameVerifier\n-dontwarn org.openjsse.javax.net.ssl.SSLParameters\n-dontwarn org.openjsse.javax.net.ssl.SSLSocket\n-dontwarn org.openjsse.net.ssl.OpenJSSE\n\n-keepclassmembers class * implements android.os.Parcelable {\n    public static final ** CREATOR;\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n  <!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <permission\n        android:name=\"${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION\"\n        android:protectionLevel=\"signature\"\n        tools:node=\"remove\" />\n\n    <uses-permission\n        android:name=\"${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION\"\n        tools:node=\"remove\" />\n\n    <application\n        android:name=\".App\"\n        android:allowBackup=\"true\"\n        android:hasFragileUserData=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        tools:ignore=\"AllowBackup,GoogleAppIndexingWarning\"\n        tools:targetApi=\"q\">\n        <activity\n            android:name=\".ui.activity.MainActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTop\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <action android:name=\"android.intent.action.APPLICATION_PREFERENCES\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n            <meta-data\n                android:name=\"android.app.shortcuts\"\n                android:resource=\"@xml/shortcuts\" />\n        </activity>\n\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            android:exported=\"false\"\n            tools:node=\"remove\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/assets/webview/colors_dark.css",
    "content": "/* Primer Colors */\n/* Please also update colors_light.css with light mode appropriate colors when modifying this file. */\n\n:root {\n    --blue-900: #082a52;\n    --blue-800: #063366;\n    --blue-700: #0b498f;\n    --blue-600: #0c65c9;\n    --blue-500: #0d6edb;\n    --blue-400: #2e8fff;\n    --blue-300: #85beff;\n    --blue-200: #d1e6ff;\n    --blue-100: #e5f2ff;\n    --blue-000: #f5faff;\n    --gray-1000: #050505;\n    --gray-950: #0b0b0d;\n    --gray-900: #17181a;\n    --gray-850: #242528;\n    --gray-800: #2e2f37;\n    --gray-750: #383a42;\n    --gray-700: #41434e;\n    --gray-650: #4b4d58;\n    --gray-600: #525560;\n    --gray-550: #5e616e;\n    --gray-500: #6c6f7e;\n    --gray-450: #787c8c;\n    --gray-400: #9194a1;\n    --gray-350: #a9abb6;\n    --gray-300: #bfc1c9;\n    --gray-250: #d6d7dc;\n    --gray-200: #e3e4e8;\n    --gray-150: #eff0f5;\n    --gray-100: #f7f7f9;\n    --gray-050: #fbfbfc;\n    --gray-000: #ffffff;\n    --green-900: #184d25;\n    --green-800: #1b612b;\n    --green-700: #1e7533;\n    --green-600: #2b8f43;\n    --green-500: #32b24f;\n    --green-400: #40d663;\n    --green-300: #95f0ab;\n    --green-200: #cbf7d5;\n    --green-100: #e5ffeb;\n    --green-000: #f5fff8;\n    --yellow-900: #755f13;\n    --yellow-800: #b89007;\n    --yellow-700: #e0b112;\n    --yellow-600: #ffcb1a;\n    --yellow-500: #ffd74d;\n    --yellow-400: #ffe166;\n    --yellow-300: #ffec8a;\n    --yellow-200: #fff6b8;\n    --yellow-100: #fffce5;\n    --yellow-000: #fffef5;\n    --orange-900: #a84603;\n    --orange-800: #c75204;\n    --orange-700: #d65c09;\n    --orange-600: #eb680e;\n    --orange-500: #fa6f0f;\n    --orange-400: #ff8a38;\n    --orange-300: #ffae75;\n    --orange-200: #ffd5b2;\n    --orange-100: #ffeee0;\n    --orange-000: #fffaf5;\n    --red-900: #8f1d22;\n    --red-800: #a82229;\n    --red-700: #bd222d;\n    --red-600: #d62b38;\n    --red-500: #e04352;\n    --red-400: #f55363;\n    --red-300: #ff808d;\n    --red-200: #ffb2bb;\n    --red-100: #ffe0e4;\n    --red-000: #fff0f2;\n    --pink-900: #702653;\n    --pink-800: #9e3674;\n    --pink-700: #c2428e;\n    --pink-600: #d63c99;\n    --pink-500: #f051b0;\n    --pink-400: #f576c2;\n    --pink-300: #fa9bd4;\n    --pink-200: #ffbde4;\n    --pink-100: #fee0f2;\n    --pink-000: #fff0f9;\n    --purple-900: #2e1757;\n    --purple-800: #3f2175;\n    --purple-700: #522e8f;\n    --purple-600: #6139a8;\n    --purple-500: #7548c7;\n    --purple-400: #916bd6;\n    --purple-300: #b899f0;\n    --purple-200: #dac7ff;\n    --purple-100: #eae0ff;\n    --purple-000: #f6f2ff;\n    --textPrimary: var(--gray-050);\n    --textSecondary: var(--gray-300);\n    --textTertiary: var(--gray-400);\n    --textPlaceholder: rgba(145, 148, 161, 0.5);\n    --link: var(--blue-400);\n    --appBackground: var(--gray-1000);\n    --backgroundSecondary: var(--gray-900);\n    --backgroundTertiary: var(--gray-850);\n    --border: rgba(191, 193, 201, 0.16);\n    --borderOpaque: var(--gray-700);\n    --iconPrimary: var(--gray-300);\n    --iconSecondary: var(--gray-500);\n    --inputBackground: rgba(191, 193, 201, 0.12);\n    --backgroundPrimary: var(--gray-1000);\n    --backgroundElevatedPrimary: var(--gray-900);\n    --backgroundElevatedSecondary: rgba(191, 193, 201, 0.04);\n    --backgroundElevatedTertiary: rgba(191, 193, 201, 0.08);\n    --backgroundInset: var(--gray-900);\n    --link-hover: var(--gray-800);\n    --color-icon-success: var(--green-600);\n    --color-text-danger: var(--red-600);\n    --diffLineNumberAdditionBackground: #08260f;\n    --diffLineNumberAdditionText: #95f0ab;\n    --diffLineAdditionBackground: #061c0b;\n    --diffLineNumberDeletionBackground: #3b0507;\n    --diffLineNumberDeletionText: #ff808d;\n    --diffLineDeletionBackground: #300406;\n    --suggestedChangeDeletionText: #ffffff;\n    --suggestedChangeAdditionText: #ffffff;\n    --suggestedChangeDeletionBackground: rgba(218,54,51,0.6);\n    --suggestedChangeAdditionBackground: rgba(46,160,67,0.6);\n    --videoBackground: #000000;\n}\n\n/* Custom Base Styles */\n\n:root {\n    --link-highlight: rgba(46, 143, 255, 0.08);\n    --pre-background: var(--backgroundElevatedTertiary);\n    --code-background: var(--pre-background);\n    --hr-background: var(--borderOpaque);\n    --thead-background: var(--pre-background);\n    --thead-border: var(--hr-background);\n    --tr-border: var(--gray-300);\n    --tr-alt-background: var(--pre-background);\n    --kbd-background: var(--pre-background);\n    --kbd-color: var(--gray-350);\n    --kbd-border: var(--gray-750);\n    --blockquote-color: var(--gray-400);\n    --blockquote-border: var(--hr-background);\n    --heading-color: var(--gray-100);\n    --h6-color: var(--gray-500);\n    --frame-border: var(--hr-background);\n    --frame-color: var(--gray-200);\n    --mention-color: var(--textPrimary);\n    --email-toggle-color: var(--kbd-color);\n    --email-toggle-background: var(--blockquote-border);\n    --email-quoted-color: var(--blockquote-color);\n    --keyword-color: var(--gray-600);\n    --code-font: ui-monospace, Menlo, monospace;\n}\n"
  },
  {
    "path": "app/src/main/assets/webview/colors_light.css",
    "content": "/* Primer Colors */\n/* Please also update colors_dark.css with dark mode appropriate colors when modifying this file. */\n\n:root {\n    --blue-900: #05264c;\n    --blue-800: #032f62;\n    --blue-700: #044289;\n    --blue-600: #005cc5;\n    --blue-500: #0366d6;\n    --blue-400: #2188ff;\n    --blue-300: #79b8ff;\n    --blue-200: #c8e1ff;\n    --blue-100: #dbedff;\n    --blue-000: #f1f8ff;\n    --gray-1000: #050505;\n    --gray-950: #0b0b0d;\n    --gray-900: #17181a;\n    --gray-850: #242528;\n    --gray-800: #2f3037;\n    --gray-750: #383a42;\n    --gray-700: #41434e;\n    --gray-650: #4b4d58;\n    --gray-600: #525560;\n    --gray-550: #5e616e;\n    --gray-500: #6a6d7c;\n    --gray-450: #787c8c;\n    --gray-400: #9194a1;\n    --gray-350: #a9abb6;\n    --gray-300: #bfc1c9;\n    --gray-250: #d6d7dc;\n    --gray-200: #e3e4e8;\n    --gray-150: #eff0f5;\n    --gray-100: #f7f7f9;\n    --gray-050: #fbfbfc;\n    --gray-000: #ffffff;\n    --green-900: #144620;\n    --green-800: #165c26;\n    --green-700: #176f2c;\n    --green-600: #22863a;\n    --green-500: #28a745;\n    --green-400: #34d058;\n    --green-300: #85e89d;\n    --green-200: #bef5cb;\n    --green-100: #dcffe4;\n    --green-000: #f0fff4;\n    --yellow-900: #735c0f;\n    --yellow-800: #b08800;\n    --yellow-700: #dbab09;\n    --yellow-600: #f9c513;\n    --yellow-500: #ffd33d;\n    --yellow-400: #ffdf5d;\n    --yellow-300: #ffea7f;\n    --yellow-200: #fff5b1;\n    --yellow-100: #fffbdd;\n    --yellow-000: #fffdef;\n    --orange-900: #a04100;\n    --orange-800: #c24e00;\n    --orange-700: #d15704;\n    --orange-600: #e36209;\n    --orange-500: #f66a0a;\n    --orange-400: #fb8532;\n    --orange-300: #ffab70;\n    --orange-200: #ffd1ac;\n    --orange-100: #ffebda;\n    --orange-000: #fff8f2;\n    --red-900: #86181d;\n    --red-800: #9e1c23;\n    --red-700: #b31d28;\n    --red-600: #cb2431;\n    --red-500: #d73a49;\n    --red-400: #ea4a5a;\n    --red-300: #f97583;\n    --red-200: #fdaeb7;\n    --red-100: #ffdce0;\n    --red-000: #ffeef0;\n    --pink-900: #6d224f;\n    --pink-800: #99306f;\n    --pink-700: #b93a86;\n    --pink-600: #d03592;\n    --pink-500: #ea4aaa;\n    --pink-400: #ec6cb9;\n    --pink-300: #f692ce;\n    --pink-200: #f9b3dd;\n    --pink-100: #fedbf0;\n    --pink-000: #ffeef8;\n    --purple-900: #29134e;\n    --purple-800: #3a1d6e;\n    --purple-700: #4c2888;\n    --purple-600: #5a32a3;\n    --purple-500: #6f42c1;\n    --purple-400: #8a63d2;\n    --purple-300: #b392f0;\n    --purple-200: #d1bcf9;\n    --purple-100: #e6dcfd;\n    --purple-000: #f5f0ff;\n    --textPrimary: var(--gray-1000);\n    --textSecondary: var(--gray-700);\n    --textTertiary: var(--gray-500);\n    --textPlaceholder: rgba(82, 85, 96, 0.5);\n    --link: var(--blue-500);\n    --appBackground: var(--gray-000);\n    --backgroundSecondary: var(--gray-000);\n    --backgroundTertiary: var(--gray-000);\n    --border: rgba(65, 67, 78, 0.25);\n    --borderOpaque: var(--gray-300);\n    --iconPrimary: var(--gray-600);\n    --iconSecondary: var(--gray-400);\n    --inputBackground: rgba(65, 67, 78, 0.12);\n    --backgroundPrimary: var(--gray-150);\n    --backgroundElevatedPrimary: var(--gray-150);\n    --backgroundElevatedSecondary: var(--gray-000);\n    --backgroundElevatedTertiary: var(--gray-000);\n    --backgroundInset: var(--gray-200);\n    --link-hover: var(--gray-200);\n    --color-icon-success: var(--green-600);\n    --color-text-danger: var(--red-600);\n    --diffLineNumberAdditionBackground: #dcffe4;\n    --diffLineNumberAdditionText: #22863a;\n    --diffLineAdditionBackground: #f0fff4;\n    --diffLineNumberDeletionBackground: #ffdce0;\n    --diffLineNumberDeletionText: #cb2431;\n    --diffLineDeletionBackground: #ffeef0;\n    --suggestedChangeDeletionText: #ffffff;\n    --suggestedChangeAdditionText: #ffffff;\n    --suggestedChangeDeletionBackground: rgba(218,54,51,0.6);\n    --suggestedChangeAdditionBackground: rgba(46,160,67,0.6);\n    --videoBackground: #000000;\n}\n\n/* Custom Base Styles */\n\n:root {\n    --link-highlight: rgba(3, 102, 214, 0.08);\n    --pre-background: var(--gray-100);\n    --code-background: var(--pre-background);\n    --hr-background: var(--gray-200);\n    --thead-background: var(--pre-background);\n    --thead-border: var(--hr-background);\n    --tr-border: var(--gray-300);\n    --tr-alt-background: var(--pre-background);\n    --kbd-background: var(--pre-background);\n    --kbd-color: var(--gray-650);\n    --kbd-border: var(--gray-250);\n    --blockquote-color: var(--gray-500);\n    --blockquote-border: var(--hr-background);\n    --heading-color: var(--gray-900);\n    --h6-color: var(--gray-500);\n    --frame-border: var(--hr-background);\n    --frame-color: var(--gray-850);\n    --mention-color: var(--gray-850);\n    --email-toggle-color: var(--kbd-color);\n    --email-toggle-background: var(--blockquote-border);\n    --email-quoted-color: var(--blockquote-color);\n    --keyword-color: var(--gray-400);\n    --code-font: ui-monospace, Menlo, monospace;\n}\n"
  },
  {
    "path": "app/src/main/assets/webview/markdown.css",
    "content": "/* Shared styles between light & dark mode so all colors should be variables */\n\n* {\n  box-sizing: border-box;\n}\n\ninput:disabled {\n  touch-action: none;\n}\n\nhtml {\n  -webkit-text-size-adjust: none;\n  text-size-adjust: none;\n  font: -apple-system-body;\n}\n\nbody {\n  color: var(--textPrimary);\n  background-color: var(--background);\n}\n\na {\n  color: var(--link);\n  text-decoration: none;\n  -webkit-tap-highlight-color: var(--link-highlight);\n  word-break: break-word;\n}\n\na:not([target]):hover {\n  border-radius: 5px;\n  background-color: var(--link-hover);\n  transition-duration: 0.2s;\n  transform: scale(1.015);\n}\n\n/*\nWeb views hold on to their hover event if the app is backgrounded. We need to disable custom hover effects by setting a\nclass on body and overriding them in CSS when we apply this workaround. When the mouse enters the web view again, we\ncan disable our override.\n*/\nbody.hover-override a:not([target]) {\n  background-color: transparent;\n  transform: scale(1);\n}\n\ndetails summary {\n  outline: 0;\n}\n\ntable {\n  border-spacing: 0;\n  border-collapse: collapse;\n}\n\nblockquote {\n  margin: 0;\n}\n\ntable, table *, pre {\n  touch-action: pan-x;\n}\n\n.markdown-body ul.contains-task-list {\n  list-style: none;\n  padding-left: 0;\n}\n\n.task-list-item {\n  padding-left: 40px;\n  margin-left: -16px;\n}\n\n.task-list-item-checkbox {\n  margin-left: -24px\n}\n\npre, code, kbd {\n  font-size: 1em;\n  font-family: var(--code-font);\n}\n\n.issue-keyword {\n  border-bottom: 1px dotted var(--keyword-color);\n}\n\n.team-mention, .user-mention {\n  font-weight: 600;\n  color: var(--mention-color);\n  white-space: nowrap;\n}\n\n.email-hidden-toggle, .email-hidden-reply {\n  display: none;\n}\n\n/* Fix checkboxes looking cut off when they render larger than the default size */\ninput[type=\"checkbox\"] {\n  transform: translate(0px);\n}\n\n/* --- */\n\n.markdown-body {\n  font-size: inherit;\n  line-height: 1.5;\n  word-wrap: break-word;\n}\n\n.markdown-body kbd {\n  display: inline-block;\n  padding: 0.18em 0.31em;\n  font-size: 0.7em;\n  line-height: 1.2em;\n  color: var(--kbd-color);\n  vertical-align: middle;\n  background-color: var(--kbd-background);\n  border: 1px solid var(--kbd-border);\n  border-radius: 0.25em;\n  box-shadow: inset 0 -1px 0 var(--kbd-border);\n  margin-right: 2px;\n}\n\n.markdown-body:after, .markdown-body:before {\n  display: table;\n  content: \"\"\n}\n\n.markdown-body:after {\n  clear: both;\n}\n\n.markdown-body > :first-child {\n  margin-top: 0 !important;\n}\n\n.markdown-body > :last-child {\n  margin-bottom: 0 !important;\n}\n\n.markdown-body a:not([href]) {\n  color: inherit;\n  text-decoration: none;\n}\n\n.markdown-body .absent {\n  color: var(--red-600);\n}\n\n.markdown-body .anchor {\n  float: left;\n  padding-right: 4px;\n  margin-left: -20px;\n  line-height: 1;\n}\n\n.markdown-body .anchor:focus {\n  outline: none;\n}\n\n.markdown-body blockquote, .markdown-body details, .markdown-body dl, .markdown-body ol, .markdown-body p, .markdown-body pre, .markdown-body table, .markdown-body ul {\n  margin-top: 0;\n  margin-bottom: 16px;\n}\n\n.markdown-body hr {\n  height: .25em;\n  padding: 0;\n  margin: 24px 0;\n  background-color: var(--hr-background);\n  border: 0;\n}\n\n.markdown-body blockquote {\n  padding-left: 1em;\n  color: var(--blockquote-color);\n  position: relative;\n}\n\n.markdown-body blockquote::before {\n  content: '';\n  width: 2px;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  background-color: var(--blockquote-border);\n  border-radius: 2px;\n}\n\n.markdown-body blockquote > :first-child {\n  margin-top: 0;\n}\n\n.markdown-body blockquote > :last-child {\n  margin-bottom: 0;\n}\n\n.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {\n  margin-top: 24px;\n  margin-bottom: 16px;\n  font-weight: 600;\n  line-height: 1.25;\n}\n\n.markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link {\n  color: var(--heading-color);\n  vertical-align: middle;\n  visibility: hidden;\n}\n\n.markdown-body h1:hover .anchor, .markdown-body h2:hover .anchor, .markdown-body h3:hover .anchor, .markdown-body h4:hover .anchor, .markdown-body h5:hover .anchor, .markdown-body h6:hover .anchor {\n  text-decoration: none;\n}\n\n.markdown-body h1:hover .anchor .octicon-link, .markdown-body h2:hover .anchor .octicon-link, .markdown-body h3:hover .anchor .octicon-link, .markdown-body h4:hover .anchor .octicon-link, .markdown-body h5:hover .anchor .octicon-link, .markdown-body h6:hover .anchor .octicon-link {\n  visibility: visible;\n}\n\n.markdown-body h1 code, .markdown-body h1 tt, .markdown-body h2 code, .markdown-body h2 tt, .markdown-body h3 code, .markdown-body h3 tt, .markdown-body h4 code, .markdown-body h4 tt, .markdown-body h5 code, .markdown-body h5 tt, .markdown-body h6 code, .markdown-body h6 tt {\n  font-size: inherit;\n}\n\n.markdown-body h1 {\n  font-size: 2em;\n}\n\n.markdown-body h1, .markdown-body h2 {\n  padding-bottom: .3em;\n  border-bottom: 1px solid var(--border);\n}\n\n.markdown-body h2 {\n  font-size: 1.5em;\n}\n\n.markdown-body h3 {\n  font-size: 1.25em;\n}\n\n.markdown-body h4 {\n  font-size: 1em;\n}\n\n.markdown-body h5 {\n  font-size: .875em;\n}\n\n.markdown-body h6 {\n  font-size: .85em;\n  color: var(--h6-color);\n}\n\n.markdown-body ul {\n  padding-left: 1.5em;\n}\n\n.markdown-body ol.no-list, .markdown-body ul.no-list {\n  padding: 0;\n  list-style-type: none;\n}\n\n.markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul {\n  margin-top: 0;\n  margin-bottom: 0;\n}\n\n.markdown-body li {\n  word-wrap: break-all;\n}\n\n.markdown-body li > p {\n  margin-top: 16px;\n}\n\n.markdown-body li + li {\n  margin-top: .25em;\n}\n\n.markdown-body dl {\n  padding: 0;\n}\n\n.markdown-body dl dt {\n  padding: 0;\n  margin-top: 16px;\n  font-size: 1em;\n  font-style: italic;\n  font-weight: 600;\n}\n\n.markdown-body dl dd {\n  padding: 0 16px;\n  margin-bottom: 16px;\n}\n\n.markdown-body table {\n  display: block;\n  width: 100%;\n  overflow: auto;\n}\n\n.markdown-body table th {\n  font-weight: 600;\n}\n\n.markdown-body table td, .markdown-body table th {\n  padding: 6px 13px;\n  border: 1px solid var(--thead-border);\n}\n\n.markdown-body table tr {\n  background-color: var(--background);\n  border-top: 1px solid var(--tr-border);\n}\n\n.markdown-body table tr:nth-child(2n) {\n  background-color: var(--tr-alt-background);\n}\n\n.markdown-body table img {\n  background-color: initial;\n}\n\n.markdown-body img {\n  max-width: 100%;\n  box-sizing: initial;\n  background-color: var(--background);\n}\n\n.markdown-body img[align=right] {\n  padding-left: 20px;\n}\n\n.markdown-body img[align=left] {\n  padding-right: 20px;\n}\n\n.markdown-body video {\n  max-width: 100%;\n  box-sizing: initial;\n  background-color: var(--videoBackground);\n}\n\n.markdown-body .emoji {\n  max-width: none;\n  vertical-align: text-top;\n  background-color: initial;\n}\n\n.markdown-body span.frame {\n  display: block;\n  overflow: hidden;\n}\n\n.markdown-body span.frame > span {\n  display: block;\n  float: left;\n  width: auto;\n  padding: 7px;\n  margin: 13px 0 0;\n  overflow: hidden;\n  border: 1px solid var(--frame-border);\n}\n\n.markdown-body span.frame span img {\n  display: block;\n  float: left;\n}\n\n.markdown-body span.frame span span {\n  display: block;\n  padding: 5px 0 0;\n  clear: both;\n  color: var(--frame-color);\n}\n\n.markdown-body span.align-center {\n  display: block;\n  overflow: hidden;\n  clear: both;\n}\n\n.markdown-body span.align-center > span {\n  display: block;\n  margin: 13px auto 0;\n  overflow: hidden;\n  text-align: center;\n}\n\n.markdown-body span.align-center span img {\n  margin: 0 auto;\n  text-align: center;\n}\n\n.markdown-body span.align-right {\n  display: block;\n  overflow: hidden;\n  clear: both;\n}\n\n.markdown-body span.align-right > span {\n  display: block;\n  margin: 13px 0 0;\n  overflow: hidden;\n  text-align: right;\n}\n\n.markdown-body span.align-right span img {\n  margin: 0;\n  text-align: right;\n}\n\n.markdown-body span.float-left {\n  display: block;\n  float: left;\n  margin-right: 13px;\n  overflow: hidden;\n}\n\n.markdown-body span.float-left span {\n  margin: 13px 0 0;\n}\n\n.markdown-body span.float-right {\n  display: block;\n  float: right;\n  margin-left: 13px;\n  overflow: hidden;\n}\n\n.markdown-body span.float-right > span {\n  display: block;\n  margin: 13px auto 0;\n  overflow: hidden;\n  text-align: right;\n}\n\n.markdown-body code, .markdown-body tt {\n  padding: .2em .4em;\n  margin: 0;\n  font-size: 85%;\n  background-color: var(--code-background);\n  border-radius: 6px;\n}\n\n.markdown-body code br, .markdown-body tt br {\n  display: none;\n}\n\n.markdown-body del code {\n  text-decoration: inherit;\n}\n\n.markdown-body pre {\n  word-wrap: normal;\n}\n\n.markdown-body pre > code {\n  padding: 0;\n  margin: 0;\n  font-size: 100%;\n  word-break: normal;\n  white-space: pre;\n  background: transparent;\n  border: 0;\n}\n\n.markdown-body .highlight {\n  margin-bottom: 16px;\n}\n\n.markdown-body .highlight pre {\n  margin-bottom: 0;\n  word-break: normal;\n}\n\n.markdown-body .highlight pre, .markdown-body pre {\n  padding: 16px;\n  overflow: auto;\n  font-size: 85%;\n  line-height: 1.45;\n  background-color: var(--pre-background);\n  border-radius: 6px;\n}\n\n.markdown-body pre code, .markdown-body pre tt {\n  display: inline;\n  max-width: auto;\n  padding: 0;\n  margin: 0;\n  overflow: visible;\n  line-height: inherit;\n  word-wrap: normal;\n  background-color: initial;\n  border: 0;\n}\n\n.markdown-body .csv-data td, .markdown-body .csv-data th {\n  padding: 5px;\n  overflow: hidden;\n  font-size: 12px;\n  line-height: 1;\n  text-align: left;\n  white-space: nowrap;\n}\n\n.markdown-body .csv-data .blob-num {\n  padding: 10px 8px 9px;\n  text-align: right;\n  background: var(--background);\n  border: 0;\n}\n\n.markdown-body .csv-data tr {\n  border-top: 0;\n}\n\n.markdown-body .csv-data th {\n  font-weight: 600;\n  background: var(--thead-background);\n  border-top: 0;\n}\n\n.open.octicon, .draft.octicon, .closed.octicon, .merged.octicon, .color-text-secondary.octicon {\n  display: inline-block;\n  margin-top: 0.15em;\n  vertical-align: text-top;\n  fill: currentColor;\n  width: 1em;\n  height: 1em;\n  font: -apple-system-body;\n}\n\n.open.octicon {\n  color: var(--color-icon-success);\n}\n\n.draft.octicon {\n  color: var(--textTertiary);\n}\n\n.closed.octicon {\n  color: var(--color-text-danger);\n}\n\n.merged.octicon {\n  color: var(--purple-500);\n}\n\n.color-text-secondary.octicon {\n  color: var(--textSecondary);\n}\n\n.reference {\n  white-space: nowrap;\n}\n\n.issue-link {\n  font-weight: 600;\n  color: var(--mention-color);\n  white-space: normal;\n}\n\n.issue-shorthand {\n  font-weight: 400;\n  color: var(--textTertiary);\n}\n\n.mr-1 {\n  margin-right: 4px;\n}\n\n.ml-1 {\n  margin-left: 4px;\n}\n\n.d-inline-block {\n  display: inline-block;\n}\n\n.v-align-middle {\n  vertical-align: middle;\n}\n\n.Box {\n  border-radius: 6px;\n}\n"
  },
  {
    "path": "app/src/main/assets/webview/syntax.css",
    "content": "/* From https://github.com/primer/github-syntax-light/blob/master/lib/github-light.css */\n.pl-c /* comment, punctuation.definition.comment, string.comment */ {\n  color: #6a737d;\n}\n\n.pl-c1 /* constant, entity.name.constant, variable.other.constant, variable.language, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header, meta.output */,\n.pl-s .pl-v /* string variable */ {\n  color: #005cc5;\n}\n\n.pl-e /* entity */,\n.pl-en /* entity.name */ {\n  color: #6f42c1;\n}\n\n.pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */,\n.pl-s .pl-s1 /* string source */ {\n  color: #24292e;\n}\n\n.pl-ent /* entity.name.tag, markup.quote */ {\n  color: #22863a;\n}\n\n.pl-k /* keyword, storage, storage.type */ {\n  color: #d73a49;\n}\n\n.pl-s /* string */,\n.pl-pds /* punctuation.definition.string, source.regexp, string.regexp.character-class */,\n.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,\n.pl-sr /* string.regexp */,\n.pl-sr .pl-cce /* string.regexp constant.character.escape */,\n.pl-sr .pl-sre /* string.regexp source.ruby.embedded */,\n.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ {\n  color: #032f62;\n}\n\n.pl-v /* variable */,\n.pl-smw /* sublimelinter.mark.warning */ {\n  color: #e36209;\n}\n\n.pl-bu /* invalid.broken, invalid.deprecated, invalid.unimplemented, message.error, brackethighlighter.unmatched, sublimelinter.mark.error */ {\n  color: #b31d28;\n}\n\n.pl-ii /* invalid.illegal */ {\n  color: #fafbfc;\n  background-color: #b31d28;\n}\n\n.pl-c2 /* carriage-return */ {\n  color: #fafbfc;\n  background-color: #d73a49;\n}\n\n.pl-c2::before /* carriage-return */ {\n  content: \"^M\";\n}\n\n.pl-sr .pl-cce /* string.regexp constant.character.escape */ {\n  font-weight: bold;\n  color: #22863a;\n}\n\n.pl-ml /* markup.list */ {\n  color: #735c0f;\n}\n\n.pl-mh /* markup.heading */,\n.pl-mh .pl-en /* markup.heading entity.name */,\n.pl-ms /* meta.separator */ {\n  font-weight: bold;\n  color: #005cc5;\n}\n\n.pl-mi /* markup.italic */ {\n  font-style: italic;\n  color: #24292e;\n}\n\n.pl-mb /* markup.bold */ {\n  font-weight: bold;\n  color: #24292e;\n}\n\n.pl-md /* markup.deleted, meta.diff.header.from-file, punctuation.definition.deleted */ {\n  color: #b31d28;\n  background-color: #ffeef0;\n}\n\n.pl-mi1 /* markup.inserted, meta.diff.header.to-file, punctuation.definition.inserted */ {\n  color: #22863a;\n  background-color: #f0fff4;\n}\n\n.pl-mc /* markup.changed, punctuation.definition.changed */ {\n  color: #e36209;\n  background-color: #ffebda;\n}\n\n.pl-mi2 /* markup.ignored, markup.untracked */ {\n  color: #f6f8fa;\n  background-color: #005cc5;\n}\n\n.pl-mdr /* meta.diff.range */ {\n  font-weight: bold;\n  color: #6f42c1;\n}\n\n.pl-ba /* brackethighlighter.tag, brackethighlighter.curly, brackethighlighter.round, brackethighlighter.square, brackethighlighter.angle, brackethighlighter.quote */ {\n  color: #586069;\n}\n\n.pl-sg /* sublimelinter.gutter-mark */ {\n  color: #959da5;\n}\n\n.pl-corl /* constant.other.reference.link, string.other.link */ {\n  text-decoration: underline;\n  color: #032f62;\n}\n"
  },
  {
    "path": "app/src/main/assets/webview/syntax_dark.css",
    "content": "/* From https://github.com/primer/github-syntax-dark/blob/master/lib/github-dark.css */\n.pl-c /* comment, punctuation.definition.comment, string.comment */ {\n  color: #959da5;\n}\n\n.pl-c1 /* constant, entity.name.constant, variable.other.constant, variable.language, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.quote, markup.raw, meta.diff.header */,\n.pl-s .pl-v /* string variable */ {\n  color: #c8e1ff;\n}\n\n.pl-e /* entity */,\n.pl-en /* entity.name */ {\n  color: #b392f0;\n}\n\n.pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */,\n.pl-s .pl-s1 /* string source */ {\n  color: #f6f8fa;\n}\n\n.pl-ent /* entity.name.tag */ {\n  color: #7bcc72;\n}\n\n.pl-k /* keyword, storage, storage.type */ {\n  color: #ea4a5a;\n}\n\n.pl-s /* string */,\n.pl-pds /* punctuation.definition.string, source.regexp, string.regexp.character-class */,\n.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,\n.pl-sr /* string.regexp */,\n.pl-sr .pl-cce /* string.regexp constant.character.escape */,\n.pl-sr .pl-sre /* string.regexp source.ruby.embedded */,\n.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ {\n  color: #79b8ff;\n}\n\n.pl-v /* variable */,\n.pl-ml /* markup.list, sublimelinter.mark.warning */ {\n  color: #fb8532;\n}\n\n.pl-bu /* invalid.broken, invalid.deprecated, invalid.unimplemented, message.error, brackethighlighter.unmatched, sublimelinter.mark.error */ {\n  color: #d73a49;\n}\n\n.pl-ii /* invalid.illegal */ {\n  color: #fafbfc;\n  background-color: #d73a49;\n}\n\n.pl-c2 /* carriage-return */ {\n  color: #fafbfc;\n  background-color: #d73a49;\n}\n\n.pl-c2::before /* carriage-return */ {\n  content: \"^M\";\n}\n\n.pl-sr .pl-cce /* string.regexp constant.character.escape */ {\n  font-weight: bold;\n  color: #7bcc72;\n}\n\n.pl-mh /* markup.heading */,\n.pl-mh .pl-en /* markup.heading entity.name */,\n.pl-ms /* meta.separator */ {\n  font-weight: bold;\n  color: #0366d6;\n}\n\n.pl-mi /* markup.italic */ {\n  font-style: italic;\n  color: #f6f8fa;\n}\n\n.pl-mb /* markup.bold */ {\n  font-weight: bold;\n  color: #f6f8fa;\n}\n\n.pl-md /* markup.deleted, meta.diff.header.from-file, punctuation.definition.deleted */ {\n  color: #ffdcd7;\n  background-color: #67060c;\n}\n\n.pl-mi1 /* markup.inserted, meta.diff.header.to-file, punctuation.definition.inserted */ {\n  color: #aff5b4;\n  background-color: #033a16;\n}\n\n.pl-mc /* markup.changed, punctuation.definition.changed */ {\n  color: #b08800;\n  background-color: #fffdef;\n}\n\n.pl-mi2 /* markup.ignored, markup.untracked */ {\n  color: #2f363d;\n  background-color: #959da5;\n}\n\n.pl-mdr /* meta.diff.range */ {\n  font-weight: bold;\n  color: #b392f0;\n}\n\n.pl-mo /* meta.output */ {\n  color: #0366d6;\n}\n\n.pl-ba /* brackethighlighter.tag, brackethighlighter.curly, brackethighlighter.round, brackethighlighter.square, brackethighlighter.angle, brackethighlighter.quote */ {\n  color: #ffeef0;\n}\n\n.pl-sg /* sublimelinter.gutter-mark */ {\n  color: #6a737d;\n}\n\n.pl-corl /* constant.other.reference.link, string.other.link */ {\n  text-decoration: underline;\n  color: #79b8ff;\n}\n"
  },
  {
    "path": "app/src/main/assets/webview/template.html",
    "content": "<!DOCTYPE html>\n<html dir=\"@dir@\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\">\n    <meta name=\"HandheldFriendly\" content=\"true\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/webview/colors_light.css\"/>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/webview/markdown.css\"/>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/webview/syntax.css\"/>\n</head>\n<body>\n    <main id=\"content\" class=\"markdown-body\">@body@</main>\n</body>\n</html>\n"
  },
  {
    "path": "app/src/main/assets/webview/template_dark.html",
    "content": "<!DOCTYPE html>\n<html dir=\"@dir@\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\">\n    <meta name=\"HandheldFriendly\" content=\"true\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/webview/colors_dark.css\"/>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/webview/markdown.css\"/>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/webview/syntax_dark.css\"/>\n</head>\n<body>\n    <main id=\"content\" class=\"markdown-body\">@body@</main>\n</body>\n</html>\n"
  },
  {
    "path": "app/src/main/java/com/google/android/material/appbar/SubtitleCollapsingToolbarLayout.java",
    "content": "package com.google.android.material.appbar;\n\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.StyleRes;\nimport androidx.appcompat.widget.Toolbar;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.graphics.drawable.DrawableCompat;\nimport androidx.core.math.MathUtils;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.google.android.material.animation.AnimationUtils;\nimport com.google.android.material.internal.DescendantOffsetUtils;\nimport com.google.android.material.internal.SubtitleCollapsingTextHelper;\nimport com.google.android.material.internal.ThemeEnforcement;\n\nimport org.lsposed.manager.R;\n\n/**\n * @see CollapsingToolbarLayout\n */\npublic class SubtitleCollapsingToolbarLayout extends FrameLayout {\n\n    private static final int DEFAULT_SCRIM_ANIMATION_DURATION = 600;\n\n    private boolean refreshToolbar = true;\n    private int toolbarId;\n    @Nullable\n    private Toolbar toolbar;\n    @Nullable\n    private View toolbarDirectChild;\n    private View dummyView;\n\n    private int expandedMarginStart;\n    private int expandedMarginTop;\n    private int expandedMarginEnd;\n    private int expandedMarginBottom;\n\n    private final Rect tmpRect = new Rect();\n    @NonNull\n    final SubtitleCollapsingTextHelper collapsingTextHelper;\n    private boolean collapsingTitleEnabled;\n    private boolean drawCollapsingTitle;\n\n    @Nullable\n    private Drawable contentScrim;\n    @Nullable\n    Drawable statusBarScrim;\n    private int scrimAlpha;\n    private boolean scrimsAreShown;\n    private ValueAnimator scrimAnimator;\n    private long scrimAnimationDuration;\n    private int scrimVisibleHeightTrigger = -1;\n\n    private AppBarLayout.OnOffsetChangedListener onOffsetChangedListener;\n\n    int currentOffset;\n\n    @Nullable\n    WindowInsetsCompat lastInsets;\n\n    public SubtitleCollapsingToolbarLayout(@NonNull Context context) {\n        this(context, null);\n    }\n\n    public SubtitleCollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SubtitleCollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        collapsingTextHelper = new SubtitleCollapsingTextHelper(this);\n        collapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);\n        collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(false);\n\n        TypedArray a = ThemeEnforcement.obtainStyledAttributes(\n                context,\n                attrs,\n                R.styleable.SubtitleCollapsingToolbarLayout,\n                defStyleAttr,\n                R.style.Widget_Design_SubtitleCollapsingToolbar);\n\n        collapsingTextHelper.setExpandedTextGravity(a.getInt(\n                R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleGravity,\n                GravityCompat.START | Gravity.BOTTOM));\n        collapsingTextHelper.setCollapsedTextGravity(a.getInt(\n                R.styleable.SubtitleCollapsingToolbarLayout_collapsedTitleGravity,\n                GravityCompat.START | Gravity.CENTER_VERTICAL));\n\n        expandedMarginStart = expandedMarginTop = expandedMarginEnd = expandedMarginBottom =\n                a.getDimensionPixelSize(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMargin, 0);\n\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginStart)) {\n            expandedMarginStart =\n                    a.getDimensionPixelSize(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginStart, 0);\n        }\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginEnd)) {\n            expandedMarginEnd =\n                    a.getDimensionPixelSize(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginEnd, 0);\n        }\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginTop)) {\n            expandedMarginTop =\n                    a.getDimensionPixelSize(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginTop, 0);\n        }\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginBottom)) {\n            expandedMarginBottom =\n                    a.getDimensionPixelSize(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleMarginBottom, 0);\n        }\n\n        collapsingTitleEnabled = a.getBoolean(R.styleable.SubtitleCollapsingToolbarLayout_titleEnabled, true);\n        setTitle(a.getText(R.styleable.SubtitleCollapsingToolbarLayout_title));\n        setSubtitle(a.getText(R.styleable.SubtitleCollapsingToolbarLayout_subtitle));\n\n        // First load the default text appearances\n        collapsingTextHelper.setExpandedTitleTextAppearance(\n                R.style.TextAppearance_Design_SubtitleCollapsingToolbar_ExpandedTitle);\n        collapsingTextHelper.setCollapsedTitleTextAppearance(\n                androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Title);\n        collapsingTextHelper.setExpandedSubtitleTextAppearance(\n                R.style.TextAppearance_Design_SubtitleCollapsingToolbar_ExpandedSubtitle);\n        collapsingTextHelper.setCollapsedSubtitleTextAppearance(\n                androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Subtitle);\n\n        // Now overlay any custom text appearances\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleTextAppearance)) {\n            collapsingTextHelper.setExpandedTitleTextAppearance(\n                    a.getResourceId(R.styleable.SubtitleCollapsingToolbarLayout_expandedTitleTextAppearance, 0));\n        }\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_collapsedTitleTextAppearance)) {\n            collapsingTextHelper.setCollapsedTitleTextAppearance(\n                    a.getResourceId(R.styleable.SubtitleCollapsingToolbarLayout_collapsedTitleTextAppearance, 0));\n        }\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_expandedSubtitleTextAppearance)) {\n            collapsingTextHelper.setExpandedSubtitleTextAppearance(\n                    a.getResourceId(R.styleable.SubtitleCollapsingToolbarLayout_expandedSubtitleTextAppearance, 0));\n        }\n        if (a.hasValue(R.styleable.SubtitleCollapsingToolbarLayout_collapsedSubtitleTextAppearance)) {\n            collapsingTextHelper.setCollapsedSubtitleTextAppearance(\n                    a.getResourceId(R.styleable.SubtitleCollapsingToolbarLayout_collapsedSubtitleTextAppearance, 0));\n        }\n\n        scrimVisibleHeightTrigger = a\n                .getDimensionPixelSize(R.styleable.SubtitleCollapsingToolbarLayout_scrimVisibleHeightTrigger, -1);\n\n        scrimAnimationDuration = a.getInt(\n                R.styleable.SubtitleCollapsingToolbarLayout_scrimAnimationDuration,\n                DEFAULT_SCRIM_ANIMATION_DURATION);\n\n        setContentScrim(a.getDrawable(R.styleable.SubtitleCollapsingToolbarLayout_contentScrim));\n        setStatusBarScrim(a.getDrawable(R.styleable.SubtitleCollapsingToolbarLayout_statusBarScrim));\n\n        toolbarId = a.getResourceId(R.styleable.SubtitleCollapsingToolbarLayout_toolbarId, -1);\n\n        a.recycle();\n\n        setWillNotDraw(false);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        // Add an OnOffsetChangedListener if possible\n        final ViewParent parent = getParent();\n        if (parent instanceof AppBarLayout) {\n            // Copy over from the ABL whether we should fit system windows\n            ViewCompat.setFitsSystemWindows(this, ViewCompat.getFitsSystemWindows((View) parent));\n\n            if (onOffsetChangedListener == null) {\n                onOffsetChangedListener = new OffsetUpdateListener();\n            }\n            ((AppBarLayout) parent).addOnOffsetChangedListener(onOffsetChangedListener);\n\n            // We're attached, so lets request an inset dispatch\n            ViewCompat.requestApplyInsets(this);\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        // Remove our OnOffsetChangedListener if possible and it exists\n        final ViewParent parent = getParent();\n        if (onOffsetChangedListener != null && parent instanceof AppBarLayout) {\n            ((AppBarLayout) parent).removeOnOffsetChangedListener(onOffsetChangedListener);\n        }\n\n        super.onDetachedFromWindow();\n    }\n\n    @Override\n    public void draw(@NonNull Canvas canvas) {\n        super.draw(canvas);\n\n        // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.\n        // Instead, we draw it here, before our collapsing text.\n        ensureToolbar();\n        if (toolbar == null && contentScrim != null && scrimAlpha > 0) {\n            contentScrim.mutate().setAlpha(scrimAlpha);\n            contentScrim.draw(canvas);\n        }\n\n        // Let the collapsing text helper draw its text\n        if (collapsingTitleEnabled && drawCollapsingTitle) {\n            collapsingTextHelper.draw(canvas);\n        }\n\n        // Now draw the status bar scrim\n        if (statusBarScrim != null && scrimAlpha > 0) {\n            final int topInset = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;\n            if (topInset > 0) {\n                statusBarScrim.setBounds(0, -currentOffset, getWidth(), topInset - currentOffset);\n                statusBarScrim.mutate().setAlpha(scrimAlpha);\n                statusBarScrim.draw(canvas);\n            }\n        }\n    }\n\n    @Override\n    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {\n        // This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),\n        // but in front of any other children which are behind it. To do this we intercept the\n        // drawChild() call, and draw our scrim just before the Toolbar is drawn\n        boolean invalidated = false;\n        if (contentScrim != null && scrimAlpha > 0 && isToolbarChild(child)) {\n            contentScrim.mutate().setAlpha(scrimAlpha);\n            contentScrim.draw(canvas);\n            invalidated = true;\n        }\n        return super.drawChild(canvas, child, drawingTime) || invalidated;\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        if (contentScrim != null) {\n            contentScrim.setBounds(0, 0, w, h);\n        }\n    }\n\n    private void ensureToolbar() {\n        if (!refreshToolbar) {\n            return;\n        }\n\n        // First clear out the current Toolbar\n        this.toolbar = null;\n        toolbarDirectChild = null;\n\n        if (toolbarId != -1) {\n            // If we have an ID set, try and find it and it's direct parent to us\n            this.toolbar = findViewById(toolbarId);\n            if (this.toolbar != null) {\n                toolbarDirectChild = findDirectChild(this.toolbar);\n            }\n        }\n\n        if (this.toolbar == null) {\n            // If we don't have an ID, or couldn't find a Toolbar with the correct ID, try and find\n            // one from our direct children\n            Toolbar toolbar = null;\n            for (int i = 0, count = getChildCount(); i < count; i++) {\n                final View child = getChildAt(i);\n                if (child instanceof Toolbar) {\n                    toolbar = (Toolbar) child;\n                    break;\n                }\n            }\n            this.toolbar = toolbar;\n        }\n\n        updateDummyView();\n        refreshToolbar = false;\n    }\n\n    private boolean isToolbarChild(View child) {\n        return (toolbarDirectChild == null || toolbarDirectChild == this)\n                ? child == toolbar\n                : child == toolbarDirectChild;\n    }\n\n    /**\n     * Returns the direct child of this layout, which itself is the ancestor of the given view.\n     */\n    @NonNull\n    private View findDirectChild(@NonNull final View descendant) {\n        View directChild = descendant;\n        for (ViewParent p = descendant.getParent(); p != this && p != null; p = p.getParent()) {\n            if (p instanceof View) {\n                directChild = (View) p;\n            }\n        }\n        return directChild;\n    }\n\n    private void updateDummyView() {\n        if (!collapsingTitleEnabled && dummyView != null) {\n            // If we have a dummy view and we have our title disabled, remove it from its parent\n            final ViewParent parent = dummyView.getParent();\n            if (parent instanceof ViewGroup) {\n                ((ViewGroup) parent).removeView(dummyView);\n            }\n        }\n        if (collapsingTitleEnabled && toolbar != null) {\n            if (dummyView == null) {\n                dummyView = new View(getContext());\n            }\n            if (dummyView.getParent() == null) {\n                toolbar.addView(dummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n            }\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        ensureToolbar();\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        final int mode = MeasureSpec.getMode(heightMeasureSpec);\n        final int topInset = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;\n        if (mode == MeasureSpec.UNSPECIFIED && topInset > 0) {\n            // If we have a top inset and we're set to wrap_content height we need to make sure\n            // we add the top inset to our height, therefore we re-measure\n            heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() + topInset, MeasureSpec.EXACTLY);\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        }\n\n        // Set our minimum height to enable proper AppBarLayout collapsing\n        if (toolbar != null) {\n            if (toolbarDirectChild == null || toolbarDirectChild == this) {\n                setMinimumHeight(getHeightWithMargins(toolbar));\n            } else {\n                setMinimumHeight(getHeightWithMargins(toolbarDirectChild));\n            }\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n\n        if (lastInsets != null) {\n            // Shift down any views which are not set to fit system windows\n            final int insetTop = lastInsets.getSystemWindowInsetTop();\n            for (int i = 0, z = getChildCount(); i < z; i++) {\n                final View child = getChildAt(i);\n                if (!ViewCompat.getFitsSystemWindows(child)) {\n                    if (child.getTop() < insetTop) {\n                        // If the child isn't set to fit system windows but is drawing within\n                        // the inset offset it down\n                        ViewCompat.offsetTopAndBottom(child, insetTop);\n                    }\n                }\n            }\n        }\n\n        // Update our child view offset helpers so that they track the correct layout coordinates\n        for (int i = 0, z = getChildCount(); i < z; i++) {\n            getViewOffsetHelper(getChildAt(i)).onViewLayout();\n        }\n\n        // Update the collapsed bounds by getting its transformed bounds\n        if (collapsingTitleEnabled && dummyView != null) {\n            // We only draw the title if the dummy view is being displayed (Toolbar removes\n            // views if there is no space)\n            drawCollapsingTitle = ViewCompat.isAttachedToWindow(dummyView) && dummyView.getVisibility() == VISIBLE;\n\n            if (drawCollapsingTitle) {\n                final boolean isRtl = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;\n\n                // Update the collapsed bounds\n                final int maxOffset =\n                        getMaxOffsetForPinChild(toolbarDirectChild != null ? toolbarDirectChild : toolbar);\n                DescendantOffsetUtils.getDescendantRect(this, dummyView, tmpRect);\n                collapsingTextHelper.setCollapsedBounds(\n                        tmpRect.left + (isRtl ? toolbar.getTitleMarginEnd() : toolbar.getTitleMarginStart()),\n                        tmpRect.top + maxOffset + toolbar.getTitleMarginTop(),\n                        tmpRect.right - (isRtl ? toolbar.getTitleMarginStart() : toolbar.getTitleMarginEnd()),\n                        tmpRect.bottom + maxOffset - toolbar.getTitleMarginBottom());\n\n                // Update the expanded bounds\n                collapsingTextHelper.setExpandedBounds(\n                        isRtl ? expandedMarginEnd : expandedMarginStart,\n                        tmpRect.top + expandedMarginTop,\n                        right - left - (isRtl ? expandedMarginStart : expandedMarginEnd),\n                        bottom - top - expandedMarginBottom);\n                // Now recalculate using the new bounds\n                collapsingTextHelper.recalculate();\n            }\n        }\n\n        if (toolbar != null) {\n            if (collapsingTitleEnabled && TextUtils.isEmpty(collapsingTextHelper.getTitle())) {\n                // If we do not currently have a title, try and grab it from the Toolbar\n                setTitle(toolbar.getTitle());\n                setSubtitle(toolbar.getSubtitle());\n            }\n        }\n\n        updateScrimVisibility();\n\n        // Apply any view offsets, this should be done at the very end of layout\n        for (int i = 0, z = getChildCount(); i < z; i++) {\n            getViewOffsetHelper(getChildAt(i)).applyOffsets();\n        }\n    }\n\n    private static int getHeightWithMargins(@NonNull final View view) {\n        final ViewGroup.LayoutParams lp = view.getLayoutParams();\n        if (lp instanceof MarginLayoutParams) {\n            final MarginLayoutParams mlp = (MarginLayoutParams) lp;\n            return view.getMeasuredHeight() + mlp.topMargin + mlp.bottomMargin;\n        }\n        return view.getMeasuredHeight();\n    }\n\n    static ViewOffsetHelper getViewOffsetHelper(View view) {\n        ViewOffsetHelper offsetHelper = (ViewOffsetHelper) view.getTag(com.google.android.material.R.id.view_offset_helper);\n        if (offsetHelper == null) {\n            offsetHelper = new ViewOffsetHelper(view);\n            view.setTag(com.google.android.material.R.id.view_offset_helper, offsetHelper);\n        }\n        return offsetHelper;\n    }\n\n    /**\n     * Sets the title to be displayed by this view, if enabled.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_title\n     * @see #setTitleEnabled(boolean)\n     * @see #getTitle()\n     */\n    public void setTitle(@Nullable CharSequence title) {\n        collapsingTextHelper.setTitle(title);\n        updateContentDescriptionFromTitle();\n    }\n\n    /**\n     * Returns the title currently being displayed by this view. If the title is not enabled, then\n     * this will return {@code null}.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_title\n     */\n    @Nullable\n    public CharSequence getTitle() {\n        return collapsingTitleEnabled ? collapsingTextHelper.getTitle() : null;\n    }\n\n    /**\n     * Sets the subtitle to be displayed by this view, if enabled.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_subtitle\n     * @see #setTitleEnabled(boolean)\n     * @see #getSubtitle()\n     */\n    public void setSubtitle(@Nullable CharSequence subtitle) {\n        collapsingTextHelper.setSubtitle(subtitle);\n        updateContentDescriptionFromTitle();\n    }\n\n    /**\n     * Returns the subtitle currently being displayed by this view. If the title is not enabled, then\n     * this will return {@code null}.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_subtitle\n     */\n    @Nullable\n    public CharSequence getSubtitle() {\n        return collapsingTitleEnabled ? collapsingTextHelper.getSubtitle() : null;\n    }\n\n    /**\n     * Sets whether this view should display its own title and subtitle.\n     * <p>\n     * <p>The title and subtitle displayed by this view will shrink and grow based on the scroll offset.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_titleEnabled\n     * @see #setTitle(CharSequence)\n     * @see #setSubtitle(CharSequence)\n     * @see #isTitleEnabled()\n     */\n    public void setTitleEnabled(boolean enabled) {\n        if (enabled != collapsingTitleEnabled) {\n            collapsingTitleEnabled = enabled;\n            updateContentDescriptionFromTitle();\n            updateDummyView();\n            requestLayout();\n        }\n    }\n\n    /**\n     * Returns whether this view is currently displaying its own title and subtitle.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_titleEnabled\n     * @see #setTitleEnabled(boolean)\n     */\n    public boolean isTitleEnabled() {\n        return collapsingTitleEnabled;\n    }\n\n    /**\n     * Set whether the content scrim and/or status bar scrim should be shown or not. Any change in the\n     * vertical scroll may overwrite this value. Any visibility change will be animated if this view\n     * has already been laid out.\n     *\n     * @param shown whether the scrims should be shown\n     * @see #getStatusBarScrim()\n     * @see #getContentScrim()\n     */\n    public void setScrimsShown(boolean shown) {\n        setScrimsShown(shown, ViewCompat.isLaidOut(this) && !isInEditMode());\n    }\n\n    /**\n     * Set whether the content scrim and/or status bar scrim should be shown or not. Any change in the\n     * vertical scroll may overwrite this value.\n     *\n     * @param shown   whether the scrims should be shown\n     * @param animate whether to animate the visibility change\n     * @see #getStatusBarScrim()\n     * @see #getContentScrim()\n     */\n    public void setScrimsShown(boolean shown, boolean animate) {\n        if (scrimsAreShown != shown) {\n            if (animate) {\n                animateScrim(shown ? 0xFF : 0x0);\n            } else {\n                setScrimAlpha(shown ? 0xFF : 0x0);\n            }\n            scrimsAreShown = shown;\n        }\n    }\n\n    private void animateScrim(int targetAlpha) {\n        ensureToolbar();\n        if (scrimAnimator == null) {\n            scrimAnimator = new ValueAnimator();\n            scrimAnimator.setDuration(scrimAnimationDuration);\n            scrimAnimator.setInterpolator(targetAlpha > scrimAlpha\n                    ? AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR\n                    : AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);\n            scrimAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animator) {\n                    setScrimAlpha((int) animator.getAnimatedValue());\n                }\n            });\n        } else if (scrimAnimator.isRunning()) {\n            scrimAnimator.cancel();\n        }\n\n        scrimAnimator.setIntValues(scrimAlpha, targetAlpha);\n        scrimAnimator.start();\n    }\n\n    void setScrimAlpha(int alpha) {\n        if (alpha != scrimAlpha) {\n            final Drawable contentScrim = this.contentScrim;\n            if (contentScrim != null && toolbar != null) {\n                ViewCompat.postInvalidateOnAnimation(toolbar);\n            }\n            scrimAlpha = alpha;\n            ViewCompat.postInvalidateOnAnimation(SubtitleCollapsingToolbarLayout.this);\n        }\n    }\n\n    int getScrimAlpha() {\n        return scrimAlpha;\n    }\n\n    /**\n     * Set the drawable to use for the content scrim from resources. Providing null will disable the\n     * scrim functionality.\n     *\n     * @param drawable the drawable to display\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_contentScrim\n     * @see #getContentScrim()\n     */\n    public void setContentScrim(@Nullable Drawable drawable) {\n        if (contentScrim != drawable) {\n            if (contentScrim != null) {\n                contentScrim.setCallback(null);\n            }\n            contentScrim = drawable != null ? drawable.mutate() : null;\n            if (contentScrim != null) {\n                contentScrim.setBounds(0, 0, getWidth(), getHeight());\n                contentScrim.setCallback(this);\n                contentScrim.setAlpha(scrimAlpha);\n            }\n            ViewCompat.postInvalidateOnAnimation(this);\n        }\n    }\n\n    /**\n     * Set the color to use for the content scrim.\n     *\n     * @param color the color to display\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_contentScrim\n     * @see #getContentScrim()\n     */\n    public void setContentScrimColor(@ColorInt int color) {\n        setContentScrim(new ColorDrawable(color));\n    }\n\n    /**\n     * Set the drawable to use for the content scrim from resources.\n     *\n     * @param resId drawable resource id\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_contentScrim\n     * @see #getContentScrim()\n     */\n    public void setContentScrimResource(@DrawableRes int resId) {\n        setContentScrim(ContextCompat.getDrawable(getContext(), resId));\n    }\n\n    /**\n     * Returns the drawable which is used for the foreground scrim.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_contentScrim\n     * @see #setContentScrim(Drawable)\n     */\n    @Nullable\n    public Drawable getContentScrim() {\n        return contentScrim;\n    }\n\n    /**\n     * Set the drawable to use for the status bar scrim from resources. Providing null will disable\n     * the scrim functionality.\n     * <p>\n     * <p>This scrim is only shown when we have been given a top system inset.\n     *\n     * @param drawable the drawable to display\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_statusBarScrim\n     * @see #getStatusBarScrim()\n     */\n    public void setStatusBarScrim(@Nullable Drawable drawable) {\n        if (statusBarScrim != drawable) {\n            if (statusBarScrim != null) {\n                statusBarScrim.setCallback(null);\n            }\n            statusBarScrim = drawable != null ? drawable.mutate() : null;\n            if (statusBarScrim != null) {\n                if (statusBarScrim.isStateful()) {\n                    statusBarScrim.setState(getDrawableState());\n                }\n                DrawableCompat.setLayoutDirection(statusBarScrim, ViewCompat.getLayoutDirection(this));\n                statusBarScrim.setVisible(getVisibility() == VISIBLE, false);\n                statusBarScrim.setCallback(this);\n                statusBarScrim.setAlpha(scrimAlpha);\n            }\n            ViewCompat.postInvalidateOnAnimation(this);\n        }\n    }\n\n    @Override\n    protected void drawableStateChanged() {\n        super.drawableStateChanged();\n\n        final int[] state = getDrawableState();\n        boolean changed = false;\n\n        Drawable d = statusBarScrim;\n        if (d != null && d.isStateful()) {\n            changed |= d.setState(state);\n        }\n        d = contentScrim;\n        if (d != null && d.isStateful()) {\n            changed |= d.setState(state);\n        }\n        if (collapsingTextHelper != null) {\n            changed |= collapsingTextHelper.setState(state);\n        }\n\n        if (changed) {\n            invalidate();\n        }\n    }\n\n    @Override\n    protected boolean verifyDrawable(Drawable who) {\n        return super.verifyDrawable(who) || who == contentScrim || who == statusBarScrim;\n    }\n\n    @Override\n    public void setVisibility(int visibility) {\n        super.setVisibility(visibility);\n\n        final boolean visible = visibility == VISIBLE;\n        if (statusBarScrim != null && statusBarScrim.isVisible() != visible) {\n            statusBarScrim.setVisible(visible, false);\n        }\n        if (contentScrim != null && contentScrim.isVisible() != visible) {\n            contentScrim.setVisible(visible, false);\n        }\n    }\n\n    /**\n     * Set the color to use for the status bar scrim.\n     * <p>\n     * <p>This scrim is only shown when we have been given a top system inset.\n     *\n     * @param color the color to display\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_statusBarScrim\n     * @see #getStatusBarScrim()\n     */\n    public void setStatusBarScrimColor(@ColorInt int color) {\n        setStatusBarScrim(new ColorDrawable(color));\n    }\n\n    /**\n     * Set the drawable to use for the content scrim from resources.\n     *\n     * @param resId drawable resource id\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_statusBarScrim\n     * @see #getStatusBarScrim()\n     */\n    public void setStatusBarScrimResource(@DrawableRes int resId) {\n        setStatusBarScrim(ContextCompat.getDrawable(getContext(), resId));\n    }\n\n    /**\n     * Returns the drawable which is used for the status bar scrim.\n     *\n     * @attr ref R.styleable#SubtitleCollapsingToolbarLayout_statusBarScrim\n     * @see #setStatusBarScrim(Drawable)\n     */\n    @Nullable\n    public Drawable getStatusBarScrim() {\n        return statusBarScrim;\n    }\n\n    /**\n     * Sets the text color and size for the collapsed title from the specified TextAppearance\n     * resource.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_collapsedTitleTextAppearance\n     */\n    public void setCollapsedTitleTextAppearance(@StyleRes int resId) {\n        collapsingTextHelper.setCollapsedTitleTextAppearance(resId);\n    }\n\n    /**\n     * Sets the text color of the collapsed title.\n     *\n     * @param color The new text color in ARGB format\n     */\n    public void setCollapsedTitleTextColor(@ColorInt int color) {\n        setCollapsedTitleTextColor(ColorStateList.valueOf(color));\n    }\n\n    /**\n     * Sets the text colors of the collapsed title.\n     *\n     * @param colors ColorStateList containing the new text colors\n     */\n    public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) {\n        collapsingTextHelper.setCollapsedTitleTextColor(colors);\n    }\n\n    /**\n     * Sets the text color and size for the collapsed subtitle from the specified TextAppearance\n     * resource.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_collapsedSubtitleTextAppearance\n     */\n    public void setCollapsedSubtitleTextAppearance(@StyleRes int resId) {\n        collapsingTextHelper.setCollapsedSubtitleTextAppearance(resId);\n    }\n\n    /**\n     * Sets the text color of the collapsed subtitle.\n     *\n     * @param color The new text color in ARGB format\n     */\n    public void setCollapsedSubtitleTextColor(@ColorInt int color) {\n        setCollapsedSubtitleTextColor(ColorStateList.valueOf(color));\n    }\n\n    /**\n     * Sets the text colors of the collapsed subtitle.\n     *\n     * @param colors ColorStateList containing the new text colors\n     */\n    public void setCollapsedSubtitleTextColor(@NonNull ColorStateList colors) {\n        collapsingTextHelper.setCollapsedSubtitleTextColor(colors);\n    }\n\n    /**\n     * Sets the horizontal alignment of the collapsed title and the vertical gravity that will be used\n     * when there is extra space in the collapsed bounds beyond what is required for the title itself.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_collapsedTitleGravity\n     */\n    public void setCollapsedTitleGravity(int gravity) {\n        collapsingTextHelper.setCollapsedTextGravity(gravity);\n    }\n\n    /**\n     * Returns the horizontal and vertical alignment for title when collapsed.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_collapsedTitleGravity\n     */\n    public int getCollapsedTitleGravity() {\n        return collapsingTextHelper.getCollapsedTextGravity();\n    }\n\n    /**\n     * Sets the text color and size for the expanded title from the specified TextAppearance resource.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleTextAppearance\n     */\n    public void setExpandedTitleTextAppearance(@StyleRes int resId) {\n        collapsingTextHelper.setExpandedTitleTextAppearance(resId);\n    }\n\n    /**\n     * Sets the text color of the expanded title.\n     *\n     * @param color The new text color in ARGB format\n     */\n    public void setExpandedTitleTextColor(@ColorInt int color) {\n        setExpandedTitleTextColor(ColorStateList.valueOf(color));\n    }\n\n    /**\n     * Sets the text colors of the expanded title.\n     *\n     * @param colors ColorStateList containing the new text colors\n     */\n    public void setExpandedTitleTextColor(@NonNull ColorStateList colors) {\n        collapsingTextHelper.setExpandedTitleTextColor(colors);\n    }\n\n    /**\n     * Sets the text color and size for the expanded subtitle from the specified TextAppearance resource.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedSubtitleTextAppearance\n     */\n    public void setExpandedSubtitleTextAppearance(@StyleRes int resId) {\n        collapsingTextHelper.setExpandedSubtitleTextAppearance(resId);\n    }\n\n    /**\n     * Sets the text color of the expanded subtitle.\n     *\n     * @param color The new text color in ARGB format\n     */\n    public void setExpandedSubtitleTextColor(@ColorInt int color) {\n        setExpandedSubtitleTextColor(ColorStateList.valueOf(color));\n    }\n\n    /**\n     * Sets the text colors of the expanded subtitle.\n     *\n     * @param colors ColorStateList containing the new text colors\n     */\n    public void setExpandedSubtitleTextColor(@NonNull ColorStateList colors) {\n        collapsingTextHelper.setExpandedSubtitleTextColor(colors);\n    }\n\n    /**\n     * Sets the horizontal alignment of the expanded title and the vertical gravity that will be used\n     * when there is extra space in the expanded bounds beyond what is required for the title itself.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleGravity\n     */\n    public void setExpandedTitleGravity(int gravity) {\n        collapsingTextHelper.setExpandedTextGravity(gravity);\n    }\n\n    /**\n     * Returns the horizontal and vertical alignment for title when expanded.\n     *\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleGravity\n     */\n    public int getExpandedTitleGravity() {\n        return collapsingTextHelper.getExpandedTextGravity();\n    }\n\n    /**\n     * Set the typeface to use for the collapsed title.\n     *\n     * @param typeface typeface to use, or {@code null} to use the default.\n     */\n    public void setCollapsedTitleTypeface(@Nullable Typeface typeface) {\n        collapsingTextHelper.setCollapsedTitleTypeface(typeface);\n    }\n\n    /**\n     * Returns the typeface used for the collapsed title.\n     */\n    @NonNull\n    public Typeface getCollapsedTitleTypeface() {\n        return collapsingTextHelper.getCollapsedTitleTypeface();\n    }\n\n    /**\n     * Set the typeface to use for the expanded title.\n     *\n     * @param typeface typeface to use, or {@code null} to use the default.\n     */\n    public void setExpandedTitleTypeface(@Nullable Typeface typeface) {\n        collapsingTextHelper.setExpandedTitleTypeface(typeface);\n    }\n\n    /**\n     * Returns the typeface used for the expanded title.\n     */\n    @NonNull\n    public Typeface getExpandedTitleTypeface() {\n        return collapsingTextHelper.getExpandedTitleTypeface();\n    }\n\n    /**\n     * Set the typeface to use for the collapsed title.\n     *\n     * @param typeface typeface to use, or {@code null} to use the default.\n     */\n    public void setCollapsedSubtitleTypeface(@Nullable Typeface typeface) {\n        collapsingTextHelper.setCollapsedSubtitleTypeface(typeface);\n    }\n\n    /**\n     * Returns the typeface used for the collapsed title.\n     */\n    @NonNull\n    public Typeface getCollapsedSubtitleTypeface() {\n        return collapsingTextHelper.getCollapsedSubtitleTypeface();\n    }\n\n    /**\n     * Set the typeface to use for the expanded title.\n     *\n     * @param typeface typeface to use, or {@code null} to use the default.\n     */\n    public void setExpandedSubtitleTypeface(@Nullable Typeface typeface) {\n        collapsingTextHelper.setExpandedSubtitleTypeface(typeface);\n    }\n\n    /**\n     * Returns the typeface used for the expanded title.\n     */\n    @NonNull\n    public Typeface getExpandedSubtitleTypeface() {\n        return collapsingTextHelper.getExpandedSubtitleTypeface();\n    }\n\n    /**\n     * Sets the expanded title margins.\n     *\n     * @param start  the starting title margin in pixels\n     * @param top    the top title margin in pixels\n     * @param end    the ending title margin in pixels\n     * @param bottom the bottom title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMargin\n     * @see #getExpandedTitleMarginStart()\n     * @see #getExpandedTitleMarginTop()\n     * @see #getExpandedTitleMarginEnd()\n     * @see #getExpandedTitleMarginBottom()\n     */\n    public void setExpandedTitleMargin(int start, int top, int end, int bottom) {\n        expandedMarginStart = start;\n        expandedMarginTop = top;\n        expandedMarginEnd = end;\n        expandedMarginBottom = bottom;\n        requestLayout();\n    }\n\n    /**\n     * @return the starting expanded title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginStart\n     * @see #setExpandedTitleMarginStart(int)\n     */\n    public int getExpandedTitleMarginStart() {\n        return expandedMarginStart;\n    }\n\n    /**\n     * Sets the starting expanded title margin in pixels.\n     *\n     * @param margin the starting title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginStart\n     * @see #getExpandedTitleMarginStart()\n     */\n    public void setExpandedTitleMarginStart(int margin) {\n        expandedMarginStart = margin;\n        requestLayout();\n    }\n\n    /**\n     * @return the top expanded title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginTop\n     * @see #setExpandedTitleMarginTop(int)\n     */\n    public int getExpandedTitleMarginTop() {\n        return expandedMarginTop;\n    }\n\n    /**\n     * Sets the top expanded title margin in pixels.\n     *\n     * @param margin the top title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginTop\n     * @see #getExpandedTitleMarginTop()\n     */\n    public void setExpandedTitleMarginTop(int margin) {\n        expandedMarginTop = margin;\n        requestLayout();\n    }\n\n    /**\n     * @return the ending expanded title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginEnd\n     * @see #setExpandedTitleMarginEnd(int)\n     */\n    public int getExpandedTitleMarginEnd() {\n        return expandedMarginEnd;\n    }\n\n    /**\n     * Sets the ending expanded title margin in pixels.\n     *\n     * @param margin the ending title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginEnd\n     * @see #getExpandedTitleMarginEnd()\n     */\n    public void setExpandedTitleMarginEnd(int margin) {\n        expandedMarginEnd = margin;\n        requestLayout();\n    }\n\n    /**\n     * @return the bottom expanded title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginBottom\n     * @see #setExpandedTitleMarginBottom(int)\n     */\n    public int getExpandedTitleMarginBottom() {\n        return expandedMarginBottom;\n    }\n\n    /**\n     * Sets the bottom expanded title margin in pixels.\n     *\n     * @param margin the bottom title margin in pixels\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginBottom\n     * @see #getExpandedTitleMarginBottom()\n     */\n    public void setExpandedTitleMarginBottom(int margin) {\n        expandedMarginBottom = margin;\n        requestLayout();\n    }\n\n    /**\n     * Sets whether {@code TextDirectionHeuristics} should be used to determine whether the title text\n     * is RTL. Experimental Feature.\n     */\n    public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) {\n        collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled);\n    }\n\n    /**\n     * Gets whether {@code TextDirectionHeuristics} should be used to determine whether the title text\n     * is RTL. Experimental Feature.\n     */\n    public boolean isRtlTextDirectionHeuristicsEnabled() {\n        return collapsingTextHelper.isRtlTextDirectionHeuristicsEnabled();\n    }\n\n    /**\n     * Set the amount of visible height in pixels used to define when to trigger a scrim visibility\n     * change.\n     * <p>\n     * <p>If the visible height of this view is less than the given value, the scrims will be made\n     * visible, otherwise they are hidden.\n     *\n     * @param height value in pixels used to define when to trigger a scrim visibility change\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_expandedTitleMarginEnd\n     */\n    public void setScrimVisibleHeightTrigger(@IntRange(from = 0) final int height) {\n        if (scrimVisibleHeightTrigger != height) {\n            scrimVisibleHeightTrigger = height;\n            // Update the scrim visibility\n            updateScrimVisibility();\n        }\n    }\n\n    /**\n     * Returns the amount of visible height in pixels used to define when to trigger a scrim\n     * visibility change.\n     *\n     * @see #setScrimVisibleHeightTrigger(int)\n     */\n    public int getScrimVisibleHeightTrigger() {\n        if (scrimVisibleHeightTrigger >= 0) {\n            // If we have one explicitly set, return it\n            return scrimVisibleHeightTrigger;\n        }\n\n        // Otherwise we'll use the default computed value\n        final int insetTop = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;\n\n        final int minHeight = ViewCompat.getMinimumHeight(this);\n        if (minHeight > 0) {\n            // If we have a minHeight set, lets use 2 * minHeight (capped at our height)\n            return Math.min((minHeight * 2) + insetTop, getHeight());\n        }\n\n        // If we reach here then we don't have a min height set. Instead we'll take a\n        // guess at 1/3 of our height being visible\n        return getHeight() / 3;\n    }\n\n    /**\n     * Set the duration used for scrim visibility animations.\n     *\n     * @param duration the duration to use in milliseconds\n     * @attr ref com.google.android.material.R.styleable#SubtitleCollapsingToolbarLayout_scrimAnimationDuration\n     */\n    public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) {\n        scrimAnimationDuration = duration;\n    }\n\n    /**\n     * Returns the duration in milliseconds used for scrim visibility animations.\n     */\n    public long getScrimAnimationDuration() {\n        return scrimAnimationDuration;\n    }\n\n    @Override\n    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n        return p instanceof LayoutParams;\n    }\n\n    @Override\n    protected LayoutParams generateDefaultLayoutParams() {\n        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n    }\n\n    @Override\n    public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {\n        return new LayoutParams(getContext(), attrs);\n    }\n\n    @Override\n    protected FrameLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {\n        return new LayoutParams(p);\n    }\n\n    public static class LayoutParams extends CollapsingToolbarLayout.LayoutParams {\n        public LayoutParams(Context c, AttributeSet attrs) {\n            super(c, attrs);\n        }\n\n        public LayoutParams(int width, int height) {\n            super(width, height);\n        }\n\n        public LayoutParams(int width, int height, int gravity) {\n            super(width, height, gravity);\n        }\n\n        public LayoutParams(ViewGroup.LayoutParams p) {\n            super(p);\n        }\n\n        public LayoutParams(MarginLayoutParams source) {\n            super(source);\n        }\n\n        @RequiresApi(19)\n        public LayoutParams(FrameLayout.LayoutParams source) {\n            super(source);\n        }\n    }\n\n    /**\n     * Show or hide the scrims if needed\n     */\n    final void updateScrimVisibility() {\n        if (contentScrim != null || statusBarScrim != null) {\n            setScrimsShown(getHeight() + currentOffset < getScrimVisibleHeightTrigger());\n        }\n    }\n\n    final int getMaxOffsetForPinChild(View child) {\n        final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        return getHeight() - offsetHelper.getLayoutTop() - child.getHeight() - lp.bottomMargin;\n    }\n\n    private void updateContentDescriptionFromTitle() {\n        // Set this layout's contentDescription to match the title if it's shown by CollapsingTextHelper\n        setContentDescription(getTitle());\n    }\n\n    private class OffsetUpdateListener implements AppBarLayout.OnOffsetChangedListener {\n        OffsetUpdateListener() {\n        }\n\n        @Override\n        public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {\n            currentOffset = verticalOffset;\n\n            final int insetTop = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;\n\n            for (int i = 0, z = getChildCount(); i < z; i++) {\n                final View child = getChildAt(i);\n                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);\n\n                switch (lp.collapseMode) {\n                    case LayoutParams.COLLAPSE_MODE_PIN:\n                        offsetHelper.setTopAndBottomOffset(\n                                MathUtils.clamp(-verticalOffset, 0, getMaxOffsetForPinChild(child)));\n                        break;\n                    case LayoutParams.COLLAPSE_MODE_PARALLAX:\n                        offsetHelper.setTopAndBottomOffset(Math.round(-verticalOffset * lp.parallaxMult));\n                        break;\n                    default:\n                        break;\n                }\n            }\n\n            // Show or hide the scrims if needed\n            updateScrimVisibility();\n\n            if (statusBarScrim != null && insetTop > 0) {\n                ViewCompat.postInvalidateOnAnimation(SubtitleCollapsingToolbarLayout.this);\n            }\n\n            // Update the collapsing text's fraction\n            final int expandRange = getHeight()\n                    - ViewCompat.getMinimumHeight(SubtitleCollapsingToolbarLayout.this)\n                    - insetTop;\n            collapsingTextHelper.setExpansionFraction(Math.abs(verticalOffset) / (float) expandRange);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/android/material/internal/SubtitleCollapsingTextHelper.java",
    "content": "package com.google.android.material.internal;\n\nimport android.animation.TimeInterpolator;\nimport android.content.res.ColorStateList;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.Typeface;\nimport android.os.Build;\nimport android.text.TextPaint;\nimport android.text.TextUtils;\nimport android.view.Gravity;\nimport android.view.View;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.math.MathUtils;\nimport androidx.core.text.TextDirectionHeuristicsCompat;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.ViewCompat;\n\nimport com.google.android.material.animation.AnimationUtils;\nimport com.google.android.material.resources.CancelableFontCallback;\nimport com.google.android.material.resources.TextAppearance;\n\n/**\n * Helper class for {@link com.google.android.material.appbar.SubtitleCollapsingToolbarLayout}.\n *\n * @see CollapsingTextHelper\n */\npublic final class SubtitleCollapsingTextHelper {\n\n    // Pre-JB-MR2 doesn't support HW accelerated canvas scaled title so we will workaround it\n    // by using our own texture\n    private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18;\n\n    private static final boolean DEBUG_DRAW = false;\n    @NonNull\n    private static final Paint DEBUG_DRAW_PAINT;\n\n    static {\n        DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;\n        if (DEBUG_DRAW_PAINT != null) {\n            DEBUG_DRAW_PAINT.setAntiAlias(true);\n            DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);\n        }\n    }\n\n    private final View view;\n\n    private boolean drawTitle;\n    private float expandedFraction;\n\n    @NonNull\n    private final Rect expandedBounds;\n    @NonNull\n    private final Rect collapsedBounds;\n    @NonNull\n    private final RectF currentBounds;\n    private int expandedTextGravity = Gravity.CENTER_VERTICAL;\n    private int collapsedTextGravity = Gravity.CENTER_VERTICAL;\n    private float expandedTitleTextSize, expandedSubtitleTextSize = 15;\n    private float collapsedTitleTextSize, collapsedSubtitleTextSize = 15;\n    private ColorStateList expandedTitleTextColor, expandedSubtitleTextColor;\n    private ColorStateList collapsedTitleTextColor, collapsedSubtitleTextColor;\n\n    private float expandedTitleDrawY, expandedSubtitleDrawY;\n    private float collapsedTitleDrawY, collapsedSubtitleDrawY;\n    private float expandedTitleDrawX, expandedSubtitleDrawX;\n    private float collapsedTitleDrawX, collapsedSubtitleDrawX;\n    private float currentTitleDrawX, currentSubtitleDrawX;\n    private float currentTitleDrawY, currentSubtitleDrawY;\n    private Typeface collapsedTitleTypeface, collapsedSubtitleTypeface;\n    private Typeface expandedTitleTypeface, expandedSubtitleTypeface;\n    private Typeface currentTitleTypeface, currentSubtitleTypeface;\n    private CancelableFontCallback expandedTitleFontCallback, expandedSubtitleFontCallback;\n    private CancelableFontCallback collapsedTitleFontCallback, collapsedSubtitleFontCallback;\n\n    @Nullable\n    private CharSequence title, subtitle;\n    @Nullable\n    private CharSequence titleToDraw, subtitleToDraw;\n    private boolean isRtl;\n    private boolean isRtlTextDirectionHeuristicsEnabled = true;\n\n    private boolean useTexture;\n    @Nullable\n    private Bitmap expandedTitleTexture, expandedSubtitleTexture;\n    private Paint titleTexturePaint, subtitleTexturePaint;\n    private float titleTextureAscent, subtitleTextureAscent;\n    private float titleTextureDescent, subtitleTextureDescent;\n\n    private float titleScale, subtitleScale;\n    private float currentTitleTextSize, currentSubtitleTextSize;\n\n    private int[] state;\n\n    private boolean boundsChanged;\n\n    @NonNull\n    private final TextPaint titleTextPaint, subtitleTextPaint;\n    @NonNull\n    private final TextPaint titleTmpPaint, subtitleTmpPaint;\n\n    private TimeInterpolator positionInterpolator;\n    private TimeInterpolator textSizeInterpolator;\n\n    private float collapsedTitleShadowRadius, collapsedSubtitleShadowRadius;\n    private float collapsedTitleShadowDx, collapsedSubtitleShadowDx;\n    private float collapsedTitleShadowDy, collapsedSubtitleShadowDy;\n    private ColorStateList collapsedTitleShadowColor, collapsedSubtitleShadowColor;\n\n    private float expandedTitleShadowRadius, expandedSubtitleShadowRadius;\n    private float expandedTitleShadowDx, expandedSubtitleShadowDx;\n    private float expandedTitleShadowDy, expandedSubtitleShadowDy;\n    private ColorStateList expandedTitleShadowColor, expandedSubtitleShadowColor;\n\n    public SubtitleCollapsingTextHelper(View view) {\n        this.view = view;\n\n        titleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);\n        titleTmpPaint = new TextPaint(titleTextPaint);\n        subtitleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);\n        subtitleTmpPaint = new TextPaint(subtitleTextPaint);\n\n        collapsedBounds = new Rect();\n        expandedBounds = new Rect();\n        currentBounds = new RectF();\n    }\n\n\n    public void setTextSizeInterpolator(TimeInterpolator interpolator) {\n        textSizeInterpolator = interpolator;\n        recalculate();\n    }\n\n    public void setPositionInterpolator(TimeInterpolator interpolator) {\n        positionInterpolator = interpolator;\n        recalculate();\n    }\n\n    public void setExpandedTitleTextSize(float textSize) {\n        if (expandedTitleTextSize != textSize) {\n            expandedTitleTextSize = textSize;\n            recalculate();\n        }\n    }\n\n    public void setCollapsedTitleTextSize(float textSize) {\n        if (collapsedTitleTextSize != textSize) {\n            collapsedTitleTextSize = textSize;\n            recalculate();\n        }\n    }\n\n    public void setExpandedSubtitleTextSize(float textSize) {\n        if (expandedSubtitleTextSize != textSize) {\n            expandedSubtitleTextSize = textSize;\n            recalculate();\n        }\n    }\n\n    public void setCollapsedSubtitleTextSize(float textSize) {\n        if (collapsedSubtitleTextSize != textSize) {\n            collapsedSubtitleTextSize = textSize;\n            recalculate();\n        }\n    }\n\n    public void setCollapsedTitleTextColor(ColorStateList textColor) {\n        if (collapsedTitleTextColor != textColor) {\n            collapsedTitleTextColor = textColor;\n            recalculate();\n        }\n    }\n\n    public void setExpandedTitleTextColor(ColorStateList textColor) {\n        if (expandedTitleTextColor != textColor) {\n            expandedTitleTextColor = textColor;\n            recalculate();\n        }\n    }\n\n    public void setCollapsedSubtitleTextColor(ColorStateList textColor) {\n        if (collapsedSubtitleTextColor != textColor) {\n            collapsedSubtitleTextColor = textColor;\n            recalculate();\n        }\n    }\n\n    public void setExpandedSubtitleTextColor(ColorStateList textColor) {\n        if (expandedSubtitleTextColor != textColor) {\n            expandedSubtitleTextColor = textColor;\n            recalculate();\n        }\n    }\n\n    public void setExpandedBounds(int left, int top, int right, int bottom) {\n        if (!rectEquals(expandedBounds, left, top, right, bottom)) {\n            expandedBounds.set(left, top, right, bottom);\n            boundsChanged = true;\n            onBoundsChanged();\n        }\n    }\n\n    public void setExpandedBounds(@NonNull Rect bounds) {\n        setExpandedBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);\n    }\n\n    public void setCollapsedBounds(int left, int top, int right, int bottom) {\n        if (!rectEquals(collapsedBounds, left, top, right, bottom)) {\n            collapsedBounds.set(left, top, right, bottom);\n            boundsChanged = true;\n            onBoundsChanged();\n        }\n    }\n\n    public void setCollapsedBounds(@NonNull Rect bounds) {\n        setCollapsedBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);\n    }\n\n    public void getCollapsedTitleTextActualBounds(@NonNull RectF bounds) {\n        boolean isRtl = calculateIsRtl(title);\n\n        bounds.left = !isRtl ? collapsedBounds.left : collapsedBounds.right - calculateCollapsedTitleTextWidth();\n        bounds.top = collapsedBounds.top;\n        bounds.right = !isRtl ? bounds.left + calculateCollapsedTitleTextWidth() : collapsedBounds.right;\n        bounds.bottom = collapsedBounds.top + getCollapsedTitleTextHeight();\n    }\n\n    public float calculateCollapsedTitleTextWidth() {\n        if (title == null) {\n            return 0;\n        }\n        getTitleTextPaintCollapsed(titleTmpPaint);\n        return titleTmpPaint.measureText(title, 0, title.length());\n    }\n\n    public void getCollapsedSubtitleTextActualBounds(@NonNull RectF bounds) {\n        boolean isRtl = calculateIsRtl(subtitle);\n\n        bounds.left = !isRtl ? collapsedBounds.left : collapsedBounds.right - calculateCollapsedSubtitleTextWidth();\n        bounds.top = collapsedBounds.top;\n        bounds.right = !isRtl ? bounds.left + calculateCollapsedSubtitleTextWidth() : collapsedBounds.right;\n        bounds.bottom = collapsedBounds.top + getCollapsedSubtitleTextHeight();\n    }\n\n    public float calculateCollapsedSubtitleTextWidth() {\n        if (subtitle == null) {\n            return 0;\n        }\n        getSubtitleTextPaintCollapsed(subtitleTmpPaint);\n        return subtitleTmpPaint.measureText(subtitle, 0, subtitle.length());\n    }\n\n    public float getExpandedTitleTextHeight() {\n        getTitleTextPaintExpanded(titleTmpPaint);\n        // Return expanded height measured from the baseline.\n        return -titleTmpPaint.ascent();\n    }\n\n    public float getCollapsedTitleTextHeight() {\n        getTitleTextPaintCollapsed(titleTmpPaint);\n        // Return collapsed height measured from the baseline.\n        return -titleTmpPaint.ascent();\n    }\n\n    public float getExpandedSubtitleTextHeight() {\n        getSubtitleTextPaintExpanded(subtitleTmpPaint);\n        // Return expanded height measured from the baseline.\n        return -subtitleTmpPaint.ascent();\n    }\n\n    public float getCollapsedSubtitleTextHeight() {\n        getSubtitleTextPaintCollapsed(subtitleTmpPaint);\n        // Return collapsed height measured from the baseline.\n        return -subtitleTmpPaint.ascent();\n    }\n\n    private void getTitleTextPaintExpanded(@NonNull TextPaint textPaint) {\n        textPaint.setTextSize(expandedTitleTextSize);\n        textPaint.setTypeface(expandedTitleTypeface);\n    }\n\n    private void getTitleTextPaintCollapsed(@NonNull TextPaint textPaint) {\n        textPaint.setTextSize(collapsedTitleTextSize);\n        textPaint.setTypeface(collapsedTitleTypeface);\n    }\n\n    private void getSubtitleTextPaintExpanded(@NonNull TextPaint textPaint) {\n        textPaint.setTextSize(expandedSubtitleTextSize);\n        textPaint.setTypeface(expandedSubtitleTypeface);\n    }\n\n    private void getSubtitleTextPaintCollapsed(@NonNull TextPaint textPaint) {\n        textPaint.setTextSize(collapsedSubtitleTextSize);\n        textPaint.setTypeface(collapsedSubtitleTypeface);\n    }\n\n    void onBoundsChanged() {\n        drawTitle = collapsedBounds.width() > 0\n                && collapsedBounds.height() > 0\n                && expandedBounds.width() > 0\n                && expandedBounds.height() > 0;\n    }\n\n    public void setExpandedTextGravity(int gravity) {\n        if (expandedTextGravity != gravity) {\n            expandedTextGravity = gravity;\n            recalculate();\n        }\n    }\n\n    public int getExpandedTextGravity() {\n        return expandedTextGravity;\n    }\n\n    public void setCollapsedTextGravity(int gravity) {\n        if (collapsedTextGravity != gravity) {\n            collapsedTextGravity = gravity;\n            recalculate();\n        }\n    }\n\n    public int getCollapsedTextGravity() {\n        return collapsedTextGravity;\n    }\n\n    public void setCollapsedTitleTextAppearance(int resId) {\n        TextAppearance textAppearance = new TextAppearance(view.getContext(), resId);\n\n        if (textAppearance.getTextColor() != null) {\n            collapsedTitleTextColor = textAppearance.getTextColor();\n        }\n        if (textAppearance.getTextSize() != 0) {\n            collapsedTitleTextSize = textAppearance.getTextSize();\n        }\n        if (textAppearance.shadowColor != null) {\n            collapsedTitleShadowColor = textAppearance.shadowColor;\n        }\n        collapsedTitleShadowDx = textAppearance.shadowDx;\n        collapsedTitleShadowDy = textAppearance.shadowDy;\n        collapsedTitleShadowRadius = textAppearance.shadowRadius;\n\n        // Cancel pending async fetch, if any, and replace with a new one.\n        if (collapsedTitleFontCallback != null) {\n            collapsedTitleFontCallback.cancel();\n        }\n        collapsedTitleFontCallback = new CancelableFontCallback(new CancelableFontCallback.ApplyFont() {\n            @Override\n            public void apply(Typeface font) {\n                setCollapsedTitleTypeface(font);\n            }\n        }, textAppearance.getFallbackFont());\n        textAppearance.getFontAsync(view.getContext(), collapsedTitleFontCallback);\n\n        recalculate();\n    }\n\n    public void setExpandedTitleTextAppearance(int resId) {\n        TextAppearance textAppearance = new TextAppearance(view.getContext(), resId);\n        if (textAppearance.getTextColor() != null) {\n            expandedTitleTextColor = textAppearance.getTextColor();\n        }\n        if (textAppearance.getTextSize() != 0) {\n            expandedTitleTextSize = textAppearance.getTextSize();\n        }\n        if (textAppearance.shadowColor != null) {\n            expandedTitleShadowColor = textAppearance.shadowColor;\n        }\n        expandedTitleShadowDx = textAppearance.shadowDx;\n        expandedTitleShadowDy = textAppearance.shadowDy;\n        expandedTitleShadowRadius = textAppearance.shadowRadius;\n\n        // Cancel pending async fetch, if any, and replace with a new one.\n        if (expandedTitleFontCallback != null) {\n            expandedTitleFontCallback.cancel();\n        }\n        expandedTitleFontCallback = new CancelableFontCallback(new CancelableFontCallback.ApplyFont() {\n            @Override\n            public void apply(Typeface font) {\n                setExpandedTitleTypeface(font);\n            }\n        }, textAppearance.getFallbackFont());\n        textAppearance.getFontAsync(view.getContext(), expandedTitleFontCallback);\n\n        recalculate();\n    }\n\n    public void setCollapsedSubtitleTextAppearance(int resId) {\n        TextAppearance textAppearance = new TextAppearance(view.getContext(), resId);\n\n        if (textAppearance.getTextColor() != null) {\n            collapsedSubtitleTextColor = textAppearance.getTextColor();\n        }\n        if (textAppearance.getTextSize() != 0) {\n            collapsedSubtitleTextSize = textAppearance.getTextSize();\n        }\n        if (textAppearance.shadowColor != null) {\n            collapsedSubtitleShadowColor = textAppearance.shadowColor;\n        }\n        collapsedSubtitleShadowDx = textAppearance.shadowDx;\n        collapsedSubtitleShadowDy = textAppearance.shadowDy;\n        collapsedSubtitleShadowRadius = textAppearance.shadowRadius;\n\n        // Cancel pending async fetch, if any, and replace with a new one.\n        if (collapsedSubtitleFontCallback != null) {\n            collapsedSubtitleFontCallback.cancel();\n        }\n        collapsedSubtitleFontCallback = new CancelableFontCallback(new CancelableFontCallback.ApplyFont() {\n            @Override\n            public void apply(Typeface font) {\n                setCollapsedSubtitleTypeface(font);\n            }\n        }, textAppearance.getFallbackFont());\n        textAppearance.getFontAsync(view.getContext(), collapsedSubtitleFontCallback);\n\n        recalculate();\n    }\n\n    public void setExpandedSubtitleTextAppearance(int resId) {\n        TextAppearance textAppearance = new TextAppearance(view.getContext(), resId);\n        if (textAppearance.getTextColor() != null) {\n            expandedSubtitleTextColor = textAppearance.getTextColor();\n        }\n        if (textAppearance.getTextSize() != 0) {\n            expandedSubtitleTextSize = textAppearance.getTextSize();\n        }\n        if (textAppearance.shadowColor != null) {\n            expandedSubtitleShadowColor = textAppearance.shadowColor;\n        }\n        expandedSubtitleShadowDx = textAppearance.shadowDx;\n        expandedSubtitleShadowDy = textAppearance.shadowDy;\n        expandedSubtitleShadowRadius = textAppearance.shadowRadius;\n\n        // Cancel pending async fetch, if any, and replace with a new one.\n        if (expandedSubtitleFontCallback != null) {\n            expandedSubtitleFontCallback.cancel();\n        }\n        expandedSubtitleFontCallback = new CancelableFontCallback(new CancelableFontCallback.ApplyFont() {\n            @Override\n            public void apply(Typeface font) {\n                if (font != null) setExpandedSubtitleTypeface(font);\n            }\n        }, null);\n        textAppearance.getFontAsync(view.getContext(), expandedSubtitleFontCallback);\n\n        recalculate();\n    }\n\n    public void setCollapsedTitleTypeface(Typeface typeface) {\n        if (setCollapsedTitleTypefaceInternal(typeface)) {\n            recalculate();\n        }\n    }\n\n    public void setExpandedTitleTypeface(Typeface typeface) {\n        if (setExpandedTitleTypefaceInternal(typeface)) {\n            recalculate();\n        }\n    }\n\n    public void setCollapsedSubtitleTypeface(Typeface typeface) {\n        if (setCollapsedSubtitleTypefaceInternal(typeface)) {\n            recalculate();\n        }\n    }\n\n    public void setExpandedSubtitleTypeface(Typeface typeface) {\n        if (setExpandedSubtitleTypefaceInternal(typeface)) {\n            recalculate();\n        }\n    }\n\n    public void setTitleTypefaces(Typeface typeface) {\n        boolean collapsedFontChanged = setCollapsedTitleTypefaceInternal(typeface);\n        boolean expandedFontChanged = setExpandedTitleTypefaceInternal(typeface);\n        if (collapsedFontChanged || expandedFontChanged) {\n            recalculate();\n        }\n    }\n\n    public void setSubtitleTypefaces(Typeface typeface) {\n        boolean collapsedFontChanged = setCollapsedSubtitleTypefaceInternal(typeface);\n        boolean expandedFontChanged = setExpandedSubtitleTypefaceInternal(typeface);\n        if (collapsedFontChanged || expandedFontChanged) {\n            recalculate();\n        }\n    }\n\n    @SuppressWarnings(\"ReferenceEquality\") // Matches the Typeface comparison in TextView\n    private boolean setCollapsedTitleTypefaceInternal(Typeface typeface) {\n        // Explicit Typeface setting cancels pending async fetch, if any, to avoid old font overriding\n        // already updated one when async op comes back after a while.\n        if (collapsedTitleFontCallback != null) {\n            collapsedTitleFontCallback.cancel();\n        }\n        if (collapsedTitleTypeface != typeface) {\n            collapsedTitleTypeface = typeface;\n            return true;\n        }\n        return false;\n    }\n\n    @SuppressWarnings(\"ReferenceEquality\") // Matches the Typeface comparison in TextView\n    private boolean setExpandedTitleTypefaceInternal(Typeface typeface) {\n        // Explicit Typeface setting cancels pending async fetch, if any, to avoid old font overriding\n        // already updated one when async op comes back after a while.\n        if (expandedTitleFontCallback != null) {\n            expandedTitleFontCallback.cancel();\n        }\n        if (expandedTitleTypeface != typeface) {\n            expandedTitleTypeface = typeface;\n            return true;\n        }\n        return false;\n    }\n\n    @SuppressWarnings(\"ReferenceEquality\") // Matches the Typeface comparison in TextView\n    private boolean setCollapsedSubtitleTypefaceInternal(Typeface typeface) {\n        // Explicit Typeface setting cancels pending async fetch, if any, to avoid old font overriding\n        // already updated one when async op comes back after a while.\n        if (collapsedSubtitleFontCallback != null) {\n            collapsedSubtitleFontCallback.cancel();\n        }\n        if (collapsedSubtitleTypeface != typeface) {\n            collapsedSubtitleTypeface = typeface;\n            return true;\n        }\n        return false;\n    }\n\n    @SuppressWarnings(\"ReferenceEquality\") // Matches the Typeface comparison in TextView\n    private boolean setExpandedSubtitleTypefaceInternal(Typeface typeface) {\n        // Explicit Typeface setting cancels pending async fetch, if any, to avoid old font overriding\n        // already updated one when async op comes back after a while.\n        if (expandedSubtitleFontCallback != null) {\n            expandedSubtitleFontCallback.cancel();\n        }\n        if (expandedSubtitleTypeface != typeface) {\n            expandedSubtitleTypeface = typeface;\n            return true;\n        }\n        return false;\n    }\n\n    public Typeface getCollapsedTitleTypeface() {\n        return collapsedTitleTypeface != null ? collapsedTitleTypeface : Typeface.DEFAULT;\n    }\n\n    public Typeface getExpandedTitleTypeface() {\n        return expandedTitleTypeface != null ? expandedTitleTypeface : Typeface.DEFAULT;\n    }\n\n    public Typeface getCollapsedSubtitleTypeface() {\n        return collapsedSubtitleTypeface != null ? collapsedSubtitleTypeface : Typeface.DEFAULT;\n    }\n\n    public Typeface getExpandedSubtitleTypeface() {\n        return expandedSubtitleTypeface != null ? expandedSubtitleTypeface : Typeface.DEFAULT;\n    }\n\n    /**\n     * Set the value indicating the current scroll value. This decides how much of the background will\n     * be displayed, as well as the title metrics/positioning.\n     *\n     * <p>A value of {@code 0.0} indicates that the layout is fully expanded. A value of {@code 1.0}\n     * indicates that the layout is fully collapsed.\n     */\n    public void setExpansionFraction(float fraction) {\n        fraction = MathUtils.clamp(fraction, 0f, 1f);\n\n        if (fraction != expandedFraction) {\n            expandedFraction = fraction;\n            calculateCurrentOffsets();\n        }\n    }\n\n    public final boolean setState(final int[] state) {\n        this.state = state;\n\n        if (isStateful()) {\n            recalculate();\n            return true;\n        }\n\n        return false;\n    }\n\n    public final boolean isStateful() {\n        return (collapsedTitleTextColor != null && collapsedTitleTextColor.isStateful())\n                || (expandedTitleTextColor != null && expandedTitleTextColor.isStateful());\n    }\n\n    public float getExpansionFraction() {\n        return expandedFraction;\n    }\n\n    public float getCollapsedTitleTextSize() {\n        return collapsedTitleTextSize;\n    }\n\n    public float getExpandedTitleTextSize() {\n        return expandedTitleTextSize;\n    }\n\n    public float getCollapsedSubtitleTextSize() {\n        return collapsedSubtitleTextSize;\n    }\n\n    public float getExpandedSubtitleTextSize() {\n        return expandedSubtitleTextSize;\n    }\n\n    public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) {\n        isRtlTextDirectionHeuristicsEnabled = rtlTextDirectionHeuristicsEnabled;\n    }\n\n    public boolean isRtlTextDirectionHeuristicsEnabled() {\n        return isRtlTextDirectionHeuristicsEnabled;\n    }\n\n    private void calculateCurrentOffsets() {\n        calculateOffsets(expandedFraction);\n    }\n\n    private void calculateOffsets(final float fraction) {\n        interpolateBounds(fraction);\n        currentTitleDrawX = lerp(expandedTitleDrawX, collapsedTitleDrawX, fraction, positionInterpolator);\n        currentTitleDrawY = lerp(expandedTitleDrawY, collapsedTitleDrawY, fraction, positionInterpolator);\n        currentSubtitleDrawX = lerp(expandedSubtitleDrawX, collapsedSubtitleDrawX, fraction, positionInterpolator);\n        currentSubtitleDrawY = lerp(expandedSubtitleDrawY, collapsedSubtitleDrawY, fraction, positionInterpolator);\n\n        setInterpolatedTitleTextSize(lerp(expandedTitleTextSize, collapsedTitleTextSize, fraction, textSizeInterpolator));\n        setInterpolatedSubtitleTextSize(lerp(expandedSubtitleTextSize, collapsedSubtitleTextSize, fraction, textSizeInterpolator));\n\n        if (collapsedTitleTextColor != expandedTitleTextColor) {\n            // If the collapsed and expanded title colors are different, blend them based on the\n            // fraction\n            titleTextPaint.setColor(blendColors(getCurrentExpandedTitleTextColor(), getCurrentCollapsedTitleTextColor(), fraction));\n        } else {\n            titleTextPaint.setColor(getCurrentCollapsedTitleTextColor());\n        }\n\n        titleTextPaint.setShadowLayer(\n                lerp(expandedTitleShadowRadius, collapsedTitleShadowRadius, fraction, null),\n                lerp(expandedTitleShadowDx, collapsedTitleShadowDx, fraction, null),\n                lerp(expandedTitleShadowDy, collapsedTitleShadowDy, fraction, null),\n                blendColors(getCurrentColor(expandedTitleShadowColor), getCurrentColor(collapsedTitleShadowColor), fraction));\n\n        if (collapsedSubtitleTextColor != expandedSubtitleTextColor) {\n            // If the collapsed and expanded title colors are different, blend them based on the\n            // fraction\n            subtitleTextPaint.setColor(blendColors(getCurrentExpandedSubtitleTextColor(), getCurrentCollapsedSubtitleTextColor(), fraction));\n        } else {\n            subtitleTextPaint.setColor(getCurrentCollapsedSubtitleTextColor());\n        }\n\n        subtitleTextPaint.setShadowLayer(\n                lerp(expandedSubtitleShadowRadius, collapsedSubtitleShadowRadius, fraction, null),\n                lerp(expandedSubtitleShadowDx, collapsedSubtitleShadowDx, fraction, null),\n                lerp(expandedSubtitleShadowDy, collapsedSubtitleShadowDy, fraction, null),\n                blendColors(getCurrentColor(expandedSubtitleShadowColor), getCurrentColor(collapsedSubtitleShadowColor), fraction));\n\n        ViewCompat.postInvalidateOnAnimation(view);\n    }\n\n    @ColorInt\n    private int getCurrentExpandedTitleTextColor() {\n        return getCurrentColor(expandedTitleTextColor);\n    }\n\n    @ColorInt\n    private int getCurrentExpandedSubtitleTextColor() {\n        return getCurrentColor(expandedSubtitleTextColor);\n    }\n\n    @ColorInt\n    public int getCurrentCollapsedTitleTextColor() {\n        return getCurrentColor(collapsedTitleTextColor);\n    }\n\n    @ColorInt\n    public int getCurrentCollapsedSubtitleTextColor() {\n        return getCurrentColor(collapsedSubtitleTextColor);\n    }\n\n    @ColorInt\n    private int getCurrentColor(@Nullable ColorStateList colorStateList) {\n        if (colorStateList == null) {\n            return 0;\n        }\n        if (state != null) {\n            return colorStateList.getColorForState(state, 0);\n        }\n        return colorStateList.getDefaultColor();\n    }\n\n    private void calculateBaseOffsets() {\n        final float currentTitleSize = this.currentTitleTextSize;\n        final float currentSubtitleSize = this.currentSubtitleTextSize;\n        final boolean isTitleOnly = TextUtils.isEmpty(subtitle);\n\n        // We then calculate the collapsed title size, using the same logic\n        calculateUsingTitleTextSize(collapsedTitleTextSize);\n        calculateUsingSubtitleTextSize(collapsedSubtitleTextSize);\n        float titleWidth = titleToDraw != null ? titleTextPaint.measureText(titleToDraw, 0, titleToDraw.length()) : 0;\n        float subtitleWidth = subtitleToDraw != null ? subtitleTextPaint.measureText(subtitleToDraw, 0, subtitleToDraw.length()) : 0;\n        final int collapsedAbsGravity =\n                GravityCompat.getAbsoluteGravity(\n                        collapsedTextGravity,\n                        isRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);\n\n        // reusable dimension\n        float titleHeight = titleTextPaint.descent() - titleTextPaint.ascent();\n        float titleOffset = titleHeight / 2 - titleTextPaint.descent();\n        float subtitleHeight = subtitleTextPaint.descent() - subtitleTextPaint.ascent();\n        float subtitleOffset = subtitleHeight / 2 - subtitleTextPaint.descent();\n\n        if (isTitleOnly) {\n            switch (collapsedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {\n                case Gravity.BOTTOM:\n                    collapsedTitleDrawY = collapsedBounds.bottom;\n                    break;\n                case Gravity.TOP:\n                    collapsedTitleDrawY = collapsedBounds.top - titleTextPaint.ascent();\n                    break;\n                case Gravity.CENTER_VERTICAL:\n                default:\n                    float textHeight = titleTextPaint.descent() - titleTextPaint.ascent();\n                    float textOffset = (textHeight / 2) - titleTextPaint.descent();\n                    collapsedTitleDrawY = collapsedBounds.centerY() + textOffset;\n                    break;\n            }\n        } else {\n            final float offset = (collapsedBounds.height() - (titleHeight + subtitleHeight)) / 3;\n            collapsedTitleDrawY = collapsedBounds.top + offset - titleTextPaint.ascent();\n            collapsedSubtitleDrawY = collapsedBounds.top + offset * 2 + titleHeight - subtitleTextPaint.ascent();\n        }\n        switch (collapsedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n            case Gravity.CENTER_HORIZONTAL:\n                collapsedTitleDrawX = collapsedBounds.centerX() - (titleWidth / 2);\n                collapsedSubtitleDrawX = collapsedBounds.centerX() - (subtitleWidth / 2);\n                break;\n            case Gravity.RIGHT:\n                collapsedTitleDrawX = collapsedBounds.right - titleWidth;\n                collapsedSubtitleDrawX = collapsedBounds.right - subtitleWidth;\n                break;\n            case Gravity.LEFT:\n            default:\n                collapsedTitleDrawX = collapsedBounds.left;\n                collapsedSubtitleDrawX = collapsedBounds.left;\n                break;\n        }\n\n        calculateUsingTitleTextSize(expandedTitleTextSize);\n        calculateUsingSubtitleTextSize(expandedSubtitleTextSize);\n        titleWidth = titleToDraw != null ? titleTextPaint.measureText(titleToDraw, 0, titleToDraw.length()) : 0;\n        subtitleWidth = subtitleToDraw != null ? subtitleTextPaint.measureText(subtitleToDraw, 0, subtitleToDraw.length()) : 0;\n\n        // dimension modification\n        titleHeight = titleTextPaint.descent() - titleTextPaint.ascent();\n        titleOffset = titleHeight / 2 - titleTextPaint.descent();\n        subtitleHeight = subtitleTextPaint.descent() - subtitleTextPaint.ascent();\n        subtitleOffset = subtitleHeight / 2 - subtitleTextPaint.descent();\n\n        final int expandedAbsGravity = GravityCompat.getAbsoluteGravity(\n                expandedTextGravity,\n                isRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR\n        );\n        if (isTitleOnly) {\n            switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {\n                case Gravity.BOTTOM:\n                    expandedTitleDrawY = expandedBounds.bottom;\n                    break;\n                case Gravity.TOP:\n                    expandedTitleDrawY = expandedBounds.top - titleTextPaint.ascent();\n                    break;\n                case Gravity.CENTER_VERTICAL:\n                default:\n                    float textHeight = titleTextPaint.descent() - titleTextPaint.ascent();\n                    float textOffset = (textHeight / 2) - titleTextPaint.descent();\n                    expandedTitleDrawY = expandedBounds.centerY() + textOffset;\n                    break;\n            }\n        } else {\n            switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {\n                case Gravity.BOTTOM:\n                    expandedTitleDrawY = expandedBounds.bottom - subtitleHeight - titleOffset;\n                    expandedSubtitleDrawY = expandedBounds.bottom;\n                    break;\n                case Gravity.TOP:\n                    expandedTitleDrawY = expandedBounds.top - titleTextPaint.ascent();\n                    expandedSubtitleDrawY = expandedTitleDrawY + subtitleHeight + titleOffset;\n                    break;\n                case Gravity.CENTER_VERTICAL:\n                default:\n                    expandedTitleDrawY = expandedBounds.centerY() + titleOffset;\n                    expandedSubtitleDrawY = expandedTitleDrawY + subtitleHeight + titleOffset;\n                    break;\n            }\n        }\n        switch (expandedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n            case Gravity.CENTER_HORIZONTAL:\n                expandedTitleDrawX = expandedBounds.centerX() - (titleWidth / 2);\n                expandedSubtitleDrawX = expandedBounds.centerX() - (subtitleWidth / 2);\n                break;\n            case Gravity.RIGHT:\n                expandedTitleDrawX = expandedBounds.right - titleWidth;\n                expandedSubtitleDrawX = expandedBounds.right - subtitleWidth;\n                break;\n            case Gravity.LEFT:\n            default:\n                expandedTitleDrawX = expandedBounds.left;\n                expandedSubtitleDrawX = expandedBounds.left;\n                break;\n        }\n\n        // The bounds have changed so we need to clear the texture\n        clearTexture();\n        // Now reset the title size back to the original\n        setInterpolatedTitleTextSize(currentTitleSize);\n        setInterpolatedSubtitleTextSize(currentSubtitleSize);\n    }\n\n    private void interpolateBounds(float fraction) {\n        currentBounds.left = lerp(expandedBounds.left, collapsedBounds.left, fraction, positionInterpolator);\n        currentBounds.top = lerp(expandedTitleDrawY, collapsedTitleDrawY, fraction, positionInterpolator);\n        currentBounds.right = lerp(expandedBounds.right, collapsedBounds.right, fraction, positionInterpolator);\n        currentBounds.bottom = lerp(expandedBounds.bottom, collapsedBounds.bottom, fraction, positionInterpolator);\n    }\n\n    public void draw(@NonNull Canvas canvas) {\n        final int saveCount = canvas.save();\n\n        if (drawTitle && titleToDraw != null) {\n            float titleX = currentTitleDrawX;\n            float titleY = currentTitleDrawY;\n            float subtitleX = currentSubtitleDrawX;\n            float subtitleY = currentSubtitleDrawY;\n\n            final boolean drawTitleTexture = useTexture && expandedTitleTexture != null;\n            final boolean drawSubtitleTexture = useTexture && expandedSubtitleTexture != null;\n\n            final float titleAscent;\n            final float titleDescent;\n            if (drawTitleTexture) {\n                titleAscent = titleTextureAscent * titleScale;\n                titleDescent = titleTextureDescent * titleScale;\n            } else {\n                titleAscent = titleTextPaint.ascent() * titleScale;\n                titleDescent = titleTextPaint.descent() * titleScale;\n            }\n\n            if (DEBUG_DRAW) {\n                // Just a debug tool, which drawn a magenta rect in the text bounds\n                canvas.drawRect(currentBounds.left, titleY + titleAscent, currentBounds.right, titleY + titleDescent, DEBUG_DRAW_PAINT);\n            }\n\n            if (drawTitleTexture) {\n                titleY += titleAscent;\n            }\n\n            // additional canvas save for subtitle\n            if (subtitleToDraw != null) {\n                final int subtitleSaveCount = canvas.save();\n\n                if (subtitleScale != 1f) {\n                    canvas.scale(subtitleScale, subtitleScale, subtitleX, subtitleY);\n                }\n\n                if (drawSubtitleTexture) {\n                    // If we should use a texture, draw it instead of title\n                    canvas.drawBitmap(expandedSubtitleTexture, subtitleX, subtitleY, subtitleTexturePaint);\n                } else {\n                    canvas.drawText(subtitleToDraw, 0, subtitleToDraw.length(), subtitleX, subtitleY, subtitleTextPaint);\n                }\n                canvas.restoreToCount(subtitleSaveCount);\n            }\n\n            if (titleScale != 1f) {\n                canvas.scale(titleScale, titleScale, titleX, titleY);\n            }\n\n            if (drawTitleTexture) {\n                // If we should use a texture, draw it instead of text\n                canvas.drawBitmap(expandedTitleTexture, titleX, titleY, titleTexturePaint);\n            } else {\n                canvas.drawText(titleToDraw, 0, titleToDraw.length(), titleX, titleY, titleTextPaint);\n            }\n        }\n\n        canvas.restoreToCount(saveCount);\n    }\n\n    private boolean calculateIsRtl(@NonNull CharSequence text) {\n        final boolean defaultIsRtl = isDefaultIsRtl();\n        return isRtlTextDirectionHeuristicsEnabled\n                ? isTextDirectionHeuristicsIsRtl(text, defaultIsRtl)\n                : defaultIsRtl;\n    }\n\n    private boolean isDefaultIsRtl() {\n        return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;\n    }\n\n    private boolean isTextDirectionHeuristicsIsRtl(@NonNull CharSequence text, boolean defaultIsRtl) {\n        return (defaultIsRtl\n                ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL\n                : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR)\n                .isRtl(text, 0, text.length());\n    }\n\n    private void setInterpolatedTitleTextSize(float textSize) {\n        calculateUsingTitleTextSize(textSize);\n\n        // Use our texture if the scale isn't 1.0\n        useTexture = USE_SCALING_TEXTURE && titleScale != 1f;\n\n        if (useTexture) {\n            // Make sure we have an expanded texture if needed\n            ensureExpandedTitleTexture();\n        }\n\n        ViewCompat.postInvalidateOnAnimation(view);\n    }\n\n    private void setInterpolatedSubtitleTextSize(float textSize) {\n        calculateUsingSubtitleTextSize(textSize);\n\n        // Use our texture if the scale isn't 1.0\n        useTexture = USE_SCALING_TEXTURE && subtitleScale != 1f;\n\n        if (useTexture) {\n            // Make sure we have an expanded texture if needed\n            ensureExpandedSubtitleTexture();\n        }\n\n        ViewCompat.postInvalidateOnAnimation(view);\n    }\n\n    @SuppressWarnings(\"ReferenceEquality\") // Matches the Typeface comparison in TextView\n    private void calculateUsingTitleTextSize(final float size) {\n        if (title == null) {\n            return;\n        }\n\n        final float collapsedWidth = collapsedBounds.width();\n        final float expandedWidth = expandedBounds.width();\n\n        final float availableWidth;\n        final float newTextSize;\n        boolean updateDrawText = false;\n\n        if (isClose(size, collapsedTitleTextSize)) {\n            newTextSize = collapsedTitleTextSize;\n            titleScale = 1f;\n            if (currentTitleTypeface != collapsedTitleTypeface) {\n                currentTitleTypeface = collapsedTitleTypeface;\n                updateDrawText = true;\n            }\n            availableWidth = collapsedWidth;\n        } else {\n            newTextSize = expandedTitleTextSize;\n            if (currentTitleTypeface != expandedTitleTypeface) {\n                currentTitleTypeface = expandedTitleTypeface;\n                updateDrawText = true;\n            }\n            if (isClose(size, expandedTitleTextSize)) {\n                // If we're close to the expanded title size, snap to it and use a scale of 1\n                titleScale = 1f;\n            } else {\n                // Else, we'll scale down from the expanded title size\n                titleScale = size / expandedTitleTextSize;\n            }\n\n            final float textSizeRatio = collapsedTitleTextSize / expandedTitleTextSize;\n            // This is the size of the expanded bounds when it is scaled to match the\n            // collapsed title size\n            final float scaledDownWidth = expandedWidth * textSizeRatio;\n\n            if (scaledDownWidth > collapsedWidth) {\n                // If the scaled down size is larger than the actual collapsed width, we need to\n                // cap the available width so that when the expanded title scales down, it matches\n                // the collapsed width\n                availableWidth = Math.min(collapsedWidth / textSizeRatio, expandedWidth);\n            } else {\n                // Otherwise we'll just use the expanded width\n                availableWidth = expandedWidth;\n            }\n        }\n\n        if (availableWidth > 0) {\n            updateDrawText = (currentTitleTextSize != newTextSize) || boundsChanged || updateDrawText;\n            currentTitleTextSize = newTextSize;\n            boundsChanged = false;\n        }\n\n        if (titleToDraw == null || updateDrawText) {\n            titleTextPaint.setTextSize(currentTitleTextSize);\n            titleTextPaint.setTypeface(currentTitleTypeface);\n            // Use linear title scaling if we're scaling the canvas\n            titleTextPaint.setLinearText(titleScale != 1f);\n\n            // If we don't currently have title to draw, or the title size has changed, ellipsize...\n            final CharSequence text =\n                    TextUtils\n                            .ellipsize(this.title, titleTextPaint, availableWidth, TextUtils.TruncateAt.END);\n            if (!TextUtils.equals(text, titleToDraw)) {\n                titleToDraw = text;\n                isRtl = calculateIsRtl(titleToDraw);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"ReferenceEquality\") // Matches the Typeface comparison in TextView\n    private void calculateUsingSubtitleTextSize(final float size) {\n        if (subtitle == null) {\n            return;\n        }\n\n        final float collapsedWidth = collapsedBounds.width();\n        final float expandedWidth = expandedBounds.width();\n\n        final float availableWidth;\n        final float newTextSize;\n        boolean updateDrawText = false;\n\n        if (isClose(size, collapsedSubtitleTextSize)) {\n            newTextSize = collapsedSubtitleTextSize;\n            subtitleScale = 1f;\n            if (currentSubtitleTypeface != collapsedSubtitleTypeface) {\n                currentSubtitleTypeface = collapsedSubtitleTypeface;\n                updateDrawText = true;\n            }\n            availableWidth = collapsedWidth;\n        } else {\n            newTextSize = expandedSubtitleTextSize;\n            if (currentSubtitleTypeface != expandedSubtitleTypeface) {\n                currentSubtitleTypeface = expandedSubtitleTypeface;\n                updateDrawText = true;\n            }\n            if (isClose(size, expandedSubtitleTextSize)) {\n                // If we're close to the expanded title size, snap to it and use a scale of 1\n                subtitleScale = 1f;\n            } else {\n                // Else, we'll scale down from the expanded title size\n                subtitleScale = size / expandedSubtitleTextSize;\n            }\n\n            final float textSizeRatio = collapsedSubtitleTextSize / expandedSubtitleTextSize;\n            // This is the size of the expanded bounds when it is scaled to match the\n            // collapsed title size\n            final float scaledDownWidth = expandedWidth * textSizeRatio;\n\n            if (scaledDownWidth > collapsedWidth) {\n                // If the scaled down size is larger than the actual collapsed width, we need to\n                // cap the available width so that when the expanded title scales down, it matches\n                // the collapsed width\n                availableWidth = Math.min(collapsedWidth / textSizeRatio, expandedWidth);\n            } else {\n                // Otherwise we'll just use the expanded width\n                availableWidth = expandedWidth;\n            }\n        }\n\n        if (availableWidth > 0) {\n            updateDrawText = (currentSubtitleTextSize != newTextSize) || boundsChanged || updateDrawText;\n            currentSubtitleTextSize = newTextSize;\n            boundsChanged = false;\n        }\n\n        if (subtitleToDraw == null || updateDrawText) {\n            subtitleTextPaint.setTextSize(currentSubtitleTextSize);\n            subtitleTextPaint.setTypeface(currentSubtitleTypeface);\n            // Use linear title scaling if we're scaling the canvas\n            subtitleTextPaint.setLinearText(subtitleScale != 1f);\n\n            // If we don't currently have title to draw, or the title size has changed, ellipsize...\n            final CharSequence text =\n                    TextUtils.ellipsize(this.subtitle, subtitleTextPaint, availableWidth, TextUtils.TruncateAt.END);\n            if (!TextUtils.equals(text, subtitleToDraw)) {\n                subtitleToDraw = text;\n                isRtl = calculateIsRtl(subtitleToDraw);\n            }\n        }\n    }\n\n    private void ensureExpandedTitleTexture() {\n        if (expandedTitleTexture != null || expandedBounds.isEmpty() || TextUtils.isEmpty(titleToDraw)) {\n            return;\n        }\n\n        calculateOffsets(0f);\n        titleTextureAscent = titleTextPaint.ascent();\n        titleTextureDescent = titleTextPaint.descent();\n\n        final int w = Math.round(titleTextPaint.measureText(titleToDraw, 0, titleToDraw.length()));\n        final int h = Math.round(titleTextureDescent - titleTextureAscent);\n\n        if (w <= 0 || h <= 0) {\n            return; // If the width or height are 0, return\n        }\n\n        expandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\n\n        Canvas c = new Canvas(expandedTitleTexture);\n        c.drawText(titleToDraw, 0, titleToDraw.length(), 0, h - titleTextPaint.descent(), titleTextPaint);\n\n        if (titleTexturePaint == null) {\n            // Make sure we have a paint\n            titleTexturePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);\n        }\n    }\n\n    private void ensureExpandedSubtitleTexture() {\n        if (expandedSubtitleTexture != null || expandedBounds.isEmpty() || TextUtils.isEmpty(subtitleToDraw)) {\n            return;\n        }\n\n        calculateOffsets(0f);\n        subtitleTextureAscent = subtitleTextPaint.ascent();\n        subtitleTextureDescent = subtitleTextPaint.descent();\n\n        final int w = Math.round(subtitleTextPaint.measureText(subtitleToDraw, 0, subtitleToDraw.length()));\n        final int h = Math.round(subtitleTextureDescent - subtitleTextureAscent);\n\n        if (w <= 0 || h <= 0) {\n            return; // If the width or height are 0, return\n        }\n\n        expandedSubtitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\n\n        Canvas c = new Canvas(expandedSubtitleTexture);\n        c.drawText(subtitleToDraw, 0, subtitleToDraw.length(), 0, h - subtitleTextPaint.descent(), subtitleTextPaint);\n\n        if (subtitleTexturePaint == null) {\n            // Make sure we have a paint\n            subtitleTexturePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);\n        }\n    }\n\n    public void recalculate() {\n        if (view.getHeight() > 0 && view.getWidth() > 0) {\n            // If we've already been laid out, calculate everything now otherwise we'll wait\n            // until a layout\n            calculateBaseOffsets();\n            calculateCurrentOffsets();\n        }\n    }\n\n    /**\n     * Set the title to display\n     *\n     * @param title\n     */\n    public void setTitle(@Nullable CharSequence title) {\n        if (title == null || !title.equals(this.title)) {\n            this.title = title;\n            titleToDraw = null;\n            clearTexture();\n            recalculate();\n        }\n    }\n\n    @Nullable\n    public CharSequence getTitle() {\n        return title;\n    }\n\n    /**\n     * Set the subtitle to display\n     *\n     * @param subtitle\n     */\n    public void setSubtitle(@Nullable CharSequence subtitle) {\n        if (subtitle == null || !subtitle.equals(this.subtitle)) {\n            this.subtitle = subtitle;\n            subtitleToDraw = null;\n            clearTexture();\n            recalculate();\n        }\n    }\n\n    @Nullable\n    public CharSequence getSubtitle() {\n        return subtitle;\n    }\n\n    private void clearTexture() {\n        if (expandedTitleTexture != null) {\n            expandedTitleTexture.recycle();\n            expandedTitleTexture = null;\n        }\n        if (expandedSubtitleTexture != null) {\n            expandedSubtitleTexture.recycle();\n            expandedSubtitleTexture = null;\n        }\n    }\n\n    /**\n     * Returns true if {@code value} is 'close' to it's closest decimal value. Close is currently\n     * defined as it's difference being < 0.001.\n     */\n    private static boolean isClose(float value, float targetValue) {\n        return Math.abs(value - targetValue) < 0.001f;\n    }\n\n    public ColorStateList getExpandedTitleTextColor() {\n        return expandedTitleTextColor;\n    }\n\n    public ColorStateList getExpandedSubtitleTextColor() {\n        return expandedSubtitleTextColor;\n    }\n\n    public ColorStateList getCollapsedTitleTextColor() {\n        return collapsedTitleTextColor;\n    }\n\n    public ColorStateList getCollapsedSubtitleTextColor() {\n        return collapsedSubtitleTextColor;\n    }\n\n    /**\n     * Blend {@code color1} and {@code color2} using the given ratio.\n     *\n     * @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend,\n     *              1.0 will return {@code color2}.\n     */\n    private static int blendColors(int color1, int color2, float ratio) {\n        final float inverseRatio = 1f - ratio;\n        float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);\n        float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio);\n        float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio);\n        float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio);\n        return Color.argb((int) a, (int) r, (int) g, (int) b);\n    }\n\n    private static float lerp(\n            float startValue, float endValue, float fraction, @Nullable TimeInterpolator interpolator) {\n        if (interpolator != null) {\n            fraction = interpolator.getInterpolation(fraction);\n        }\n        return AnimationUtils.lerp(startValue, endValue, fraction);\n    }\n\n    private static boolean rectEquals(@NonNull Rect r, int left, int top, int right, int bottom) {\n        return !(r.left != left || r.top != top || r.right != right || r.bottom != bottom);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/App.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager;\n\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.content.BroadcastReceiver;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.SharedPreferences;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Process;\nimport android.provider.MediaStore;\nimport android.provider.Settings;\nimport android.system.Os;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatDelegate;\nimport androidx.preference.PreferenceManager;\n\nimport org.lsposed.hiddenapibypass.HiddenApiBypass;\nimport org.lsposed.manager.adapters.AppHelper;\nimport org.lsposed.manager.receivers.LSPManagerServiceHolder;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.util.CloudflareDNS;\nimport org.lsposed.manager.util.ModuleUtil;\nimport org.lsposed.manager.util.ThemeUtil;\nimport org.lsposed.manager.util.UpdateUtil;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.time.OffsetDateTime;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.FutureTask;\n\nimport okhttp3.Cache;\nimport okhttp3.OkHttpClient;\nimport okhttp3.logging.HttpLoggingInterceptor;\nimport rikka.core.os.FileUtils;\nimport rikka.material.app.LocaleDelegate;\n\npublic class App extends Application {\n    public static final int PER_USER_RANGE = 100000;\n    public static final FutureTask<String> HTML_TEMPLATE = new FutureTask<>(() -> readWebviewHTML(\"template.html\"));\n    public static final FutureTask<String> HTML_TEMPLATE_DARK = new FutureTask<>(() -> readWebviewHTML(\"template_dark.html\"));\n\n    private static String readWebviewHTML(String name) {\n        try {\n            var input = App.getInstance().getAssets().open(\"webview/\" + name);\n            var result = new ByteArrayOutputStream(1024);\n            FileUtils.copy(input, result);\n            return result.toString(StandardCharsets.UTF_8.name());\n        } catch (IOException e) {\n            Log.e(App.TAG, \"read webview HTML\", e);\n            return \"<html dir\\\"@dir@\\\"><body>@body@</body></html>\";\n        }\n    }\n\n    static {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            HiddenApiBypass.addHiddenApiExemptions(\"\");\n        }\n        Looper.myQueue().addIdleHandler(() -> {\n            if (App.getInstance() == null || App.getExecutorService() == null) return true;\n            App.getExecutorService().submit(() -> {\n                var list = AppHelper.getAppList(false);\n                var pm = App.getInstance().getPackageManager();\n                list.parallelStream().forEach(i -> AppHelper.getAppLabel(i, pm));\n                AppHelper.getDenyList(false);\n                ModuleUtil.getInstance();\n                RepoLoader.getInstance();\n            });\n            App.getExecutorService().submit(HTML_TEMPLATE);\n            App.getExecutorService().submit(HTML_TEMPLATE_DARK);\n            return false;\n        });\n    }\n\n    public static final String TAG = \"LSPosedManager\";\n    private static final String ACTION_USER_ADDED = \"android.intent.action.USER_ADDED\";\n    private static final String ACTION_USER_REMOVED = \"android.intent.action.USER_REMOVED\";\n    private static final String ACTION_USER_INFO_CHANGED = \"android.intent.action.USER_INFO_CHANGED\";\n    private static final String EXTRA_REMOVED_FOR_ALL_USERS = \"android.intent.extra.REMOVED_FOR_ALL_USERS\";\n    private static App instance = null;\n    private static OkHttpClient okHttpClient;\n    private static Cache okHttpCache;\n    private SharedPreferences pref;\n    private static final ExecutorService executorService = Executors.newCachedThreadPool();\n    private static final Handler MainHandler = new Handler(Looper.getMainLooper());\n\n    public static App getInstance() {\n        return instance;\n    }\n\n    public static SharedPreferences getPreferences() {\n        return instance.pref;\n    }\n\n    public static ExecutorService getExecutorService() {\n        return executorService;\n    }\n\n    public static final boolean isParasitic = !Process.isApplicationUid(Process.myUid());\n\n    public static Handler getMainHandler() {\n        return MainHandler;\n    }\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n        var map = new HashMap<String, String>(1);\n        map.put(\"isParasitic\", String.valueOf(isParasitic));\n        var am = getSystemService(ActivityManager.class);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            map.clear();\n            var reasons = am.getHistoricalProcessExitReasons(null, 0, 1);\n            if (reasons.size() == 1) {\n                map.put(\"description\", reasons.get(0).getDescription());\n                map.put(\"importance\", String.valueOf(reasons.get(0).getImportance()));\n                map.put(\"process\", reasons.get(0).getProcessName());\n                map.put(\"reason\", String.valueOf(reasons.get(0).getReason()));\n                map.put(\"status\", String.valueOf(reasons.get(0).getStatus()));\n            }\n        }\n    }\n\n    private void setCrashReport() {\n        var handler = Thread.getDefaultUncaughtExceptionHandler();\n        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {\n            var time = OffsetDateTime.now();\n            var dir = new File(getCacheDir(), \"crash\");\n            //noinspection ResultOfMethodCallIgnored\n            dir.mkdir();\n            var file = new File(dir, time.toEpochSecond() + \".log\");\n            try (var pw = new PrintWriter(file)) {\n                pw.println(BuildConfig.VERSION_NAME + \" (\" + BuildConfig.VERSION_CODE + \")\");\n                pw.println(time);\n                pw.println(\"pid: \" + Os.getpid() + \" uid: \" + Os.getuid());\n                throwable.printStackTrace(pw);\n            } catch (IOException ignored) {\n            }\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                var table = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);\n                var values = new ContentValues();\n                values.put(MediaStore.Downloads.DISPLAY_NAME, \"LSPosed_crash_report\" + time.toEpochSecond() + \".zip\");\n                values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS);\n                var cr = getContentResolver();\n                var uri = cr.insert(table, values);\n                if (uri == null) return;\n                try (var zipFd = cr.openFileDescriptor(uri, \"wt\")) {\n                    LSPManagerServiceHolder.getService().getLogs(zipFd);\n                } catch (Exception ignored) {\n                    cr.delete(uri, null, null);\n                }\n            }\n            if (handler != null) {\n                handler.uncaughtException(thread, throwable);\n            }\n        });\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        instance = this;\n\n        setCrashReport();\n        pref = PreferenceManager.getDefaultSharedPreferences(this);\n        if (!pref.contains(\"doh\")) {\n            var name = \"private_dns_mode\";\n            if (\"hostname\".equals(Settings.Global.getString(getContentResolver(), name))) {\n                pref.edit().putBoolean(\"doh\", false).apply();\n            } else {\n                pref.edit().putBoolean(\"doh\", true).apply();\n            }\n        }\n        AppCompatDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme());\n        LocaleDelegate.setDefaultLocale(getLocale());\n        var res = getResources();\n        var config = res.getConfiguration();\n        config.setLocale(LocaleDelegate.getDefaultLocale());\n        //noinspection deprecation\n        res.updateConfiguration(config, res.getDisplayMetrics());\n\n        IntentFilter intentFilter = new IntentFilter();\n        intentFilter.addAction(\"org.lsposed.manager.NOTIFICATION\");\n        registerReceiver(new BroadcastReceiver() {\n            @Override\n            public void onReceive(Context context, Intent inIntent) {\n                var intent = (Intent) inIntent.getParcelableExtra(Intent.EXTRA_INTENT);\n                Log.d(TAG, \"onReceive: \" + intent);\n                switch (intent.getAction()) {\n                    case Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED, Intent.ACTION_PACKAGE_FULLY_REMOVED, Intent.ACTION_UID_REMOVED -> {\n                        var userId = intent.getIntExtra(Intent.EXTRA_USER, 0);\n                        var packageName = intent.getStringExtra(\"android.intent.extra.PACKAGES\");\n                        var packageRemovedForAllUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);\n                        var isXposedModule = intent.getBooleanExtra(\"isXposedModule\", false);\n                        if (packageName != null) {\n                            if (isXposedModule)\n                                ModuleUtil.getInstance().reloadSingleModule(packageName, userId, packageRemovedForAllUsers);\n                            else\n                                App.getExecutorService().submit(() -> AppHelper.getAppList(true));\n                        }\n                    }\n                    case ACTION_USER_ADDED, ACTION_USER_REMOVED, ACTION_USER_INFO_CHANGED -> App.getExecutorService().submit(() -> ModuleUtil.getInstance().reloadInstalledModules());\n                }\n            }\n        }, intentFilter, Context.RECEIVER_NOT_EXPORTED);\n\n        UpdateUtil.loadRemoteVersion();\n    }\n\n    @NonNull\n    public static OkHttpClient getOkHttpClient() {\n        if (okHttpClient != null) return okHttpClient;\n        var builder = new OkHttpClient.Builder()\n            .cache(getOkHttpCache())\n            .dns(new CloudflareDNS());\n        if (BuildConfig.DEBUG) {\n            var log = new HttpLoggingInterceptor();\n            log.setLevel(HttpLoggingInterceptor.Level.HEADERS);\n            builder.addInterceptor(log);\n        }\n        okHttpClient = builder.build();\n        return okHttpClient;\n    }\n\n    @NonNull\n    public static Cache getOkHttpCache() {\n        if (okHttpCache != null) return okHttpCache;\n        long size50MiB = 50 * 1024 * 1024;\n        okHttpCache = new Cache(new File(instance.getCacheDir(), \"http_cache\"), size50MiB);\n        return okHttpCache;\n    }\n\n    public static Locale getLocale(String tag) {\n        if (TextUtils.isEmpty(tag) || \"SYSTEM\".equals(tag)) {\n            return LocaleDelegate.getSystemLocale();\n        }\n        return Locale.forLanguageTag(tag);\n    }\n\n    public static Locale getLocale() {\n        String tag = getPreferences().getString(\"language\", null);\n        return getLocale(tag);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ConfigManager.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager;\n\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.os.ParcelFileDescriptor;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport org.lsposed.lspd.ILSPManagerService;\nimport org.lsposed.lspd.models.Application;\nimport org.lsposed.lspd.models.UserInfo;\nimport org.lsposed.manager.adapters.ScopeAdapter;\nimport org.lsposed.manager.receivers.LSPManagerServiceHolder;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\npublic class ConfigManager {\n\n    public static boolean isBinderAlive() {\n        return LSPManagerServiceHolder.getService() != null;\n    }\n\n    public static int getXposedApiVersion() {\n        try {\n            return LSPManagerServiceHolder.getService().getXposedApiVersion();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return -1;\n        }\n    }\n\n    public static String getXposedVersionName() {\n        try {\n            return LSPManagerServiceHolder.getService().getXposedVersionName();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return \"\";\n        }\n    }\n\n    public static int getXposedVersionCode() {\n        try {\n            return LSPManagerServiceHolder.getService().getXposedVersionCode();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return -1;\n        }\n    }\n\n    public static List<PackageInfo> getInstalledPackagesFromAllUsers(int flags, boolean filterNoProcess) {\n        List<PackageInfo> list = new ArrayList<>();\n        try {\n            list.addAll(LSPManagerServiceHolder.getService().getInstalledPackagesFromAllUsers(flags, filterNoProcess).getList());\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n        }\n        return list;\n    }\n\n    public static String[] getEnabledModules() {\n        try {\n            return LSPManagerServiceHolder.getService().enabledModules();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return new String[0];\n        }\n    }\n\n    public static boolean setModuleEnabled(String packageName, boolean enable) {\n        try {\n            return enable ? LSPManagerServiceHolder.getService().enableModule(packageName) : LSPManagerServiceHolder.getService().disableModule(packageName);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean setModuleScope(String packageName, boolean legacy, Set<ScopeAdapter.ApplicationWithEquals> applications) {\n        try {\n            List<Application> list = new ArrayList<>();\n            applications.forEach(application -> {\n                Application app = new Application();\n                app.userId = application.userId;\n                app.packageName = application.packageName;\n                list.add(app);\n            });\n            if (legacy) {\n                Application app = new Application();\n                app.userId = 0;\n                app.packageName = packageName;\n                list.add(app);\n            }\n            return LSPManagerServiceHolder.getService().setModuleScope(packageName, list);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static List<ScopeAdapter.ApplicationWithEquals> getModuleScope(String packageName) {\n        List<ScopeAdapter.ApplicationWithEquals> list = new ArrayList<>();\n        try {\n            var applications = LSPManagerServiceHolder.getService().getModuleScope(packageName);\n            if (applications == null) {\n                return list;\n            }\n            applications.forEach(application -> {\n                if (!application.packageName.equals(packageName)) {\n                    list.add(new ScopeAdapter.ApplicationWithEquals(application));\n                }\n            });\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n        }\n        return list;\n    }\n\n    public static boolean enableStatusNotification() {\n        try {\n            return LSPManagerServiceHolder.getService().enableStatusNotification();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean setEnableStatusNotification(boolean enabled) {\n        try {\n            LSPManagerServiceHolder.getService().setEnableStatusNotification(enabled);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean isVerboseLogEnabled() {\n        try {\n            return LSPManagerServiceHolder.getService().isVerboseLog();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean setVerboseLogEnabled(boolean enabled) {\n        try {\n            LSPManagerServiceHolder.getService().setVerboseLog(enabled);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean isLogWatchdogEnabled() {\n        try {\n            return LSPManagerServiceHolder.getService().isLogWatchdogEnabled();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean setLogWatchdog(boolean enabled) {\n        try {\n            LSPManagerServiceHolder.getService().setLogWatchdog(enabled);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static ParcelFileDescriptor getLog(boolean verbose) {\n        try {\n            return verbose ? LSPManagerServiceHolder.getService().getVerboseLog() : LSPManagerServiceHolder.getService().getModulesLog();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return null;\n        }\n    }\n\n    public static boolean clearLogs(boolean verbose) {\n        try {\n            return LSPManagerServiceHolder.getService().clearLogs(verbose);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static PackageInfo getPackageInfo(String packageName, int flags, int userId) throws PackageManager.NameNotFoundException {\n        try {\n            var info = LSPManagerServiceHolder.getService().getPackageInfo(packageName, flags, userId);\n            if (info == null) throw new PackageManager.NameNotFoundException();\n            return info;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            throw new PackageManager.NameNotFoundException();\n        }\n    }\n\n    public static boolean forceStopPackage(String packageName, int userId) {\n        try {\n            LSPManagerServiceHolder.getService().forceStopPackage(packageName, userId);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean reboot() {\n        try {\n            LSPManagerServiceHolder.getService().reboot();\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean uninstallPackage(String packageName, int userId) {\n        try {\n            return LSPManagerServiceHolder.getService().uninstallPackage(packageName, userId);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean isSepolicyLoaded() {\n        try {\n            return LSPManagerServiceHolder.getService().isSepolicyLoaded();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static List<UserInfo> getUsers() {\n        try {\n            return LSPManagerServiceHolder.getService().getUsers();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return null;\n        }\n    }\n\n    public static boolean installExistingPackageAsUser(String packageName, int userId) {\n        final int INSTALL_SUCCEEDED = 1;\n        try {\n            var ret = LSPManagerServiceHolder.getService().installExistingPackageAsUser(packageName, userId);\n            return ret == INSTALL_SUCCEEDED;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean isMagiskInstalled() {\n        var path = System.getenv(\"PATH\");\n        if (path == null) return false;\n        else return Arrays.stream(path.split(File.pathSeparator))\n                .anyMatch(str -> new File(str, \"magisk\").exists());\n    }\n\n    public static boolean systemServerRequested() {\n        try {\n            return LSPManagerServiceHolder.getService().systemServerRequested();\n        } catch (RemoteException e) {\n            return false;\n        }\n    }\n\n    public static boolean dex2oatFlagsLoaded() {\n        try {\n            return LSPManagerServiceHolder.getService().dex2oatFlagsLoaded();\n        } catch (RemoteException e) {\n            return false;\n        }\n    }\n\n    public static int startActivityAsUserWithFeature(Intent intent, int userId) {\n        try {\n            return LSPManagerServiceHolder.getService().startActivityAsUserWithFeature(intent, userId);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return -1;\n        }\n    }\n\n    public static List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {\n        List<ResolveInfo> list = new ArrayList<>();\n        try {\n            list.addAll(LSPManagerServiceHolder.getService().queryIntentActivitiesAsUser(intent, flags, userId).getList());\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n        }\n        return list;\n    }\n\n    public static boolean setHiddenIcon(boolean hide) {\n        try {\n            LSPManagerServiceHolder.getService().setHiddenIcon(hide);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static String getApi() {\n        try {\n            return LSPManagerServiceHolder.getService().getApi();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return e.toString();\n        }\n    }\n\n    public static List<String> getDenyListPackages() {\n        List<String> list = new ArrayList<>();\n        try {\n            list.addAll(LSPManagerServiceHolder.getService().getDenyListPackages());\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n        }\n        return list;\n    }\n\n    public static void flashZip(String zipPath, ParcelFileDescriptor outputStream) {\n        try {\n            LSPManagerServiceHolder.getService().flashZip(zipPath, outputStream);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n        }\n    }\n\n    public static boolean isDexObfuscateEnabled() {\n        try {\n            return LSPManagerServiceHolder.getService().getDexObfuscate();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean setDexObfuscateEnabled(boolean enabled) {\n        try {\n            LSPManagerServiceHolder.getService().setDexObfuscate(enabled);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static int getDex2OatWrapperCompatibility() {\n        try {\n            return LSPManagerServiceHolder.getService().getDex2OatWrapperCompatibility();\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return ILSPManagerService.DEX2OAT_CRASHED;\n        }\n    }\n\n    public static boolean getAutoInclude(String packageName) {\n        try {\n            return LSPManagerServiceHolder.getService().getAutoInclude(packageName);\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n\n    public static boolean setAutoInclude(String packageName, boolean enable) {\n        try {\n            LSPManagerServiceHolder.getService().setAutoInclude(packageName, enable);\n            return true;\n        } catch (RemoteException e) {\n            Log.e(App.TAG, Log.getStackTraceString(e));\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/Constants.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager;\n\nimport android.os.IBinder;\n\nimport org.lsposed.manager.receivers.LSPManagerServiceHolder;\n\npublic class Constants {\n    public static boolean setBinder(IBinder binder) {\n        LSPManagerServiceHolder.init(binder);\n        return LSPManagerServiceHolder.getService().asBinder().isBinderAlive();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/adapters/AppHelper.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.adapters;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.os.Parcel;\nimport android.view.MenuItem;\n\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class AppHelper {\n\n    public static final String SETTINGS_CATEGORY = \"de.robv.android.xposed.category.MODULE_SETTINGS\";\n    public static final int FLAG_SHOW_FOR_ALL_USERS = 0x0400;\n    private static List<String> denyList;\n    private static List<PackageInfo> appList;\n    private static final ConcurrentHashMap<PackageInfo, CharSequence> appLabel = new ConcurrentHashMap<>();\n\n    @SuppressLint(\"WrongConstant\")\n    public static Intent getSettingsIntent(String packageName, int userId) {\n        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);\n        intentToResolve.addCategory(SETTINGS_CATEGORY);\n        intentToResolve.setPackage(packageName);\n\n        List<ResolveInfo> ris = ConfigManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);\n\n        if (ris.size() == 0) {\n            return getLaunchIntentForPackage(packageName, userId);\n        }\n\n        Intent intent = new Intent(intentToResolve);\n        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        intent.setClassName(ris.get(0).activityInfo.packageName,\n                ris.get(0).activityInfo.name);\n        intent.putExtra(\"lsp_no_switch_to_user\", (ris.get(0).activityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);\n        return intent;\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    public static Intent getLaunchIntentForPackage(String packageName, int userId) {\n        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);\n        intentToResolve.addCategory(Intent.CATEGORY_INFO);\n        intentToResolve.setPackage(packageName);\n        List<ResolveInfo> ris = ConfigManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);\n\n        if (ris.size() == 0) {\n            intentToResolve.removeCategory(Intent.CATEGORY_INFO);\n            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);\n            intentToResolve.setPackage(packageName);\n            ris = ConfigManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);\n        }\n\n        if (ris.size() == 0) {\n            return null;\n        }\n\n        Intent intent = new Intent(intentToResolve);\n        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        intent.setClassName(ris.get(0).activityInfo.packageName,\n                ris.get(0).activityInfo.name);\n        intent.putExtra(\"lsp_no_switch_to_user\", (ris.get(0).activityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);\n        return intent;\n    }\n\n    public static boolean onOptionsItemSelected(MenuItem item, SharedPreferences preferences) {\n        int itemId = item.getItemId();\n        int i = preferences.getInt(\"list_sort\", 0);\n        if (itemId == R.id.item_sort_by_name) {\n            i = (i % 2 == 0) ? 0 : 1;\n        } else if (itemId == R.id.item_sort_by_package_name) {\n            i = (i % 2 == 0) ? 2 : 3;\n        } else if (itemId == R.id.item_sort_by_install_time) {\n            i = (i % 2 == 0) ? 4 : 5;\n        } else if (itemId == R.id.item_sort_by_update_time) {\n            i = (i % 2 == 0) ? 6 : 7;\n        } else if (itemId == R.id.reverse) {\n            if (i % 2 == 0) i++;\n            else i--;\n        } else {\n            return false;\n        }\n        preferences.edit().putInt(\"list_sort\", i).apply();\n        if (item.isCheckable())\n            item.setChecked(!item.isChecked());\n        return true;\n    }\n\n    public static Comparator<PackageInfo> getAppListComparator(int sort, PackageManager pm) {\n        ApplicationInfo.DisplayNameComparator displayNameComparator = new ApplicationInfo.DisplayNameComparator(pm);\n        return switch (sort) {\n            case 7 ->\n                    Collections.reverseOrder(Comparator.comparingLong((PackageInfo a) -> a.lastUpdateTime));\n            case 6 -> Comparator.comparingLong((PackageInfo a) -> a.lastUpdateTime);\n            case 5 ->\n                    Collections.reverseOrder(Comparator.comparingLong((PackageInfo a) -> a.firstInstallTime));\n            case 4 -> Comparator.comparingLong((PackageInfo a) -> a.firstInstallTime);\n            case 3 -> Collections.reverseOrder(Comparator.comparing(a -> a.packageName));\n            case 2 -> Comparator.comparing(a -> a.packageName);\n            case 1 ->\n                    Collections.reverseOrder((PackageInfo a, PackageInfo b) -> displayNameComparator.compare(a.applicationInfo, b.applicationInfo));\n            default ->\n                    (PackageInfo a, PackageInfo b) -> displayNameComparator.compare(a.applicationInfo, b.applicationInfo);\n        };\n    }\n\n    synchronized public static List<PackageInfo> getAppList(boolean force) {\n        if (appList == null || force) {\n            appList = ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA | PackageManager.MATCH_UNINSTALLED_PACKAGES, true);\n            PackageInfo system = null;\n            for (var app : appList) {\n                if (\"android\".equals(app.packageName)) {\n                    var p = Parcel.obtain();\n                    app.writeToParcel(p, 0);\n                    p.setDataPosition(0);\n                    system = PackageInfo.CREATOR.createFromParcel(p);\n                    system.packageName = \"system\";\n                    system.applicationInfo.packageName = system.packageName;\n                    break;\n                }\n            }\n            if (system != null) {\n                appList.add(system);\n            }\n        }\n        return appList;\n    }\n\n    synchronized public static List<String> getDenyList(boolean force) {\n        if (denyList == null || force) {\n            denyList = ConfigManager.getDenyListPackages();\n        }\n        return denyList;\n    }\n\n    public static CharSequence getAppLabel(PackageInfo info, PackageManager pm) {\n        if (info == null || info.applicationInfo == null) return null;\n        return appLabel.computeIfAbsent(info, i -> i.applicationInfo.loadLabel(pm));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.adapters;\n\nimport static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.text.style.ForegroundColorSpan;\nimport android.text.style.StyleSpan;\nimport android.text.style.TypefaceSpan;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.CompoundButton;\nimport android.widget.Filter;\nimport android.widget.Filterable;\nimport android.widget.ImageView;\nimport android.widget.Switch;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.bumptech.glide.request.target.CustomTarget;\nimport com.bumptech.glide.request.transition.Transition;\nimport com.google.android.material.checkbox.MaterialCheckBox;\n\nimport org.lsposed.lspd.models.Application;\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.BuildConfig;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.ItemMasterSwitchBinding;\nimport org.lsposed.manager.databinding.ItemModuleBinding;\nimport org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;\nimport org.lsposed.manager.ui.fragment.AppListFragment;\nimport org.lsposed.manager.ui.fragment.CompileDialogFragment;\nimport org.lsposed.manager.ui.widget.EmptyStateRecyclerView;\nimport org.lsposed.manager.util.GlideApp;\nimport org.lsposed.manager.util.ModuleUtil;\n\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport rikka.core.util.ResourceUtils;\nimport rikka.material.app.LocaleDelegate;\nimport rikka.widget.mainswitchbar.MainSwitchBar;\nimport rikka.widget.mainswitchbar.OnMainSwitchChangeListener;\n\npublic class ScopeAdapter extends EmptyStateRecyclerView.EmptyStateAdapter<ScopeAdapter.ViewHolder> implements Filterable {\n\n    private final Activity activity;\n    private final AppListFragment fragment;\n    private final PackageManager pm;\n    private final SharedPreferences preferences;\n    private final ModuleUtil moduleUtil;\n\n    private final ModuleUtil.InstalledModule module;\n\n    private Set<ApplicationWithEquals> recommendedList = new HashSet<>();\n    private Set<ApplicationWithEquals> checkedList = new HashSet<>();\n    private List<AppInfo> searchList = new ArrayList<>();\n    private List<AppInfo> showList = new ArrayList<>();\n    private List<String> denyList = new ArrayList<>();\n\n    public RecyclerView.Adapter<RecyclerView.ViewHolder> switchAdaptor = new RecyclerView.Adapter<>() {\n        @NonNull\n        @Override\n        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            return new RecyclerView.ViewHolder(ItemMasterSwitchBinding.inflate(activity.getLayoutInflater(), parent, false).masterSwitch) {\n            };\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {\n            var mainSwitchBar = (MainSwitchBar) holder.itemView;\n            mainSwitchBar.setChecked(enabled);\n            mainSwitchBar.addOnSwitchChangeListener(switchBarOnCheckedChangeListener);\n        }\n\n        @Override\n        public int getItemCount() {\n            return 1;\n        }\n    };\n\n    private final OnMainSwitchChangeListener switchBarOnCheckedChangeListener = new OnMainSwitchChangeListener() {\n        @Override\n        public void onSwitchChanged(Switch view, boolean isChecked) {\n            enabled = isChecked;\n            if (!moduleUtil.setModuleEnabled(module.packageName, isChecked)) {\n                view.setChecked(!isChecked);\n                enabled = !isChecked;\n            }\n            var tmpChkList = new HashSet<>(checkedList);\n            if (isChecked && !tmpChkList.isEmpty() && !ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList)) {\n                view.setChecked(false);\n                enabled = false;\n            }\n            fragment.runOnUiThread(ScopeAdapter.this::notifyDataSetChanged);\n        }\n    };\n\n    private ApplicationInfo selectedApplicationInfo;\n    private boolean isLoaded = false;\n    private boolean enabled = true;\n\n    public ScopeAdapter(AppListFragment fragment, ModuleUtil.InstalledModule module) {\n        this.fragment = fragment;\n        this.activity = fragment.requireActivity();\n        this.module = module;\n        moduleUtil = ModuleUtil.getInstance();\n        preferences = App.getPreferences();\n        pm = activity.getPackageManager();\n    }\n\n    @NonNull\n    @Override\n    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        return new ViewHolder(ItemModuleBinding.inflate(activity.getLayoutInflater(), parent, false));\n    }\n\n    private boolean shouldHideApp(PackageInfo info, ApplicationWithEquals app, HashSet<ApplicationWithEquals> tmpChkList) {\n        if (info.packageName.equals(\"system\")) {\n            return false;\n        }\n        if (tmpChkList.contains(app)) {\n            return false;\n        }\n        if (preferences.getBoolean(\"filter_denylist\", false)) {\n            if (denyList.contains(info.packageName)) {\n                return true;\n            }\n        }\n        if (preferences.getBoolean(\"filter_modules\", true)) {\n            if (ModuleUtil.getInstance().getModule(info.packageName, info.applicationInfo.uid / App.PER_USER_RANGE) != null) {\n                return true;\n            }\n        }\n        if (preferences.getBoolean(\"filter_games\", true)) {\n            if (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME) {\n                return true;\n            }\n            //noinspection deprecation\n            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) != 0) {\n                return true;\n            }\n        }\n        return preferences.getBoolean(\"filter_system_apps\", true) && (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;\n    }\n\n    private int sortApps(AppInfo x, AppInfo y) {\n        Comparator<PackageInfo> comparator = AppHelper.getAppListComparator(preferences.getInt(\"list_sort\", 0), pm);\n        Comparator<AppInfo> frameworkComparator = (a, b) -> {\n            if (a.packageName.equals(\"system\") == b.packageName.equals(\"system\")) {\n                return comparator.compare(a.packageInfo, b.packageInfo);\n            } else if (a.packageName.equals(\"system\")) {\n                return -1;\n            } else {\n                return 1;\n            }\n        };\n        Comparator<AppInfo> recommendedComparator = (a, b) -> {\n            boolean aRecommended = !recommendedList.isEmpty() && recommendedList.contains(a.application);\n            boolean bRecommended = !recommendedList.isEmpty() && recommendedList.contains(b.application);\n            if (aRecommended == bRecommended) {\n                return frameworkComparator.compare(a, b);\n            } else if (aRecommended) {\n                return -1;\n            } else {\n                return 1;\n            }\n        };\n        boolean aChecked = checkedList.contains(x.application);\n        boolean bChecked = checkedList.contains(y.application);\n        if (aChecked == bChecked) {\n            return recommendedComparator.compare(x, y);\n        } else if (aChecked) {\n            return -1;\n        } else {\n            return 1;\n        }\n    }\n\n    private void checkRecommended() {\n        if (!enabled) {\n            fragment.showHint(R.string.module_is_not_activated_yet, false);\n            return;\n        }\n        fragment.runAsync(() -> {\n            var tmpChkList = new HashSet<>(checkedList);\n            tmpChkList.removeIf(i -> i.userId == module.userId);\n            tmpChkList.addAll(recommendedList);\n            ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList);\n            checkedList = tmpChkList;\n            fragment.runOnUiThread(this::notifyDataSetChanged);\n        });\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    private void setLoaded(List<AppInfo> list, boolean loaded) {\n        fragment.runOnUiThread(() -> {\n            if (list != null) showList = list;\n            isLoaded = loaded;\n            notifyDataSetChanged();\n        });\n    }\n\n    public boolean onOptionsItemSelected(MenuItem item) {\n        int itemId = item.getItemId();\n        if (itemId == R.id.use_recommended) {\n            if (!checkedList.isEmpty()) {\n                new BlurBehindDialogBuilder(activity, R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons)\n                        .setMessage(R.string.use_recommended_message)\n                        .setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended())\n                        .setNegativeButton(android.R.string.cancel, null)\n                        .show();\n            } else {\n                checkRecommended();\n            }\n            return true;\n        } else if (itemId == R.id.item_filter_system) {\n            item.setChecked(!item.isChecked());\n            preferences.edit().putBoolean(\"filter_system_apps\", item.isChecked()).apply();\n        } else if (itemId == R.id.item_filter_games) {\n            item.setChecked(!item.isChecked());\n            preferences.edit().putBoolean(\"filter_games\", item.isChecked()).apply();\n        } else if (itemId == R.id.item_filter_modules) {\n            item.setChecked(!item.isChecked());\n            preferences.edit().putBoolean(\"filter_modules\", item.isChecked()).apply();\n        } else if (itemId == R.id.item_filter_denylist) {\n            item.setChecked(!item.isChecked());\n            preferences.edit().putBoolean(\"filter_denylist\", item.isChecked()).apply();\n        } else if (itemId == R.id.backup) {\n            LocalDateTime now = LocalDateTime.now();\n            try {\n                fragment.backupLauncher.launch(String.format(LocaleDelegate.getDefaultLocale(),\n                        \"%s_%s.lsp\", module.getAppName(), now.toString()));\n                return true;\n            } catch (ActivityNotFoundException e) {\n                fragment.showHint(R.string.enable_documentui, true);\n                return false;\n            }\n        } else if (itemId == R.id.restore) {\n            try {\n                fragment.restoreLauncher.launch(new String[]{\"*/*\"});\n                return true;\n            } catch (ActivityNotFoundException e) {\n                fragment.showHint(R.string.enable_documentui, true);\n                return false;\n            }\n        } else if (itemId == R.id.select_all) {\n            var tmpChkList = new HashSet<ApplicationWithEquals>(ConfigManager.getModuleScope(module.packageName));\n            for (AppInfo info : searchList) {\n                if (info.packageName.equals(\"android\")) {\n                    fragment.showHint(R.string.reboot_required, true, R.string.reboot, v -> ConfigManager.reboot());\n                }\n                tmpChkList.add(info.application);\n            }\n            ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList);\n        } else if (itemId == R.id.select_none) {\n            var tmpChkList = new HashSet<ApplicationWithEquals>(ConfigManager.getModuleScope(module.packageName));\n            for (AppInfo info : searchList) {\n                if (tmpChkList.remove(info.application) && info.packageName.equals(\"android\")) {\n                    fragment.showHint(R.string.reboot_required, true, R.string.reboot, v -> ConfigManager.reboot());\n                }\n            }\n            ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList);\n        } else if (itemId == R.id.auto_include) {\n            item.setChecked(!item.isChecked());\n            ConfigManager.setAutoInclude(module.packageName, item.isChecked());\n        } else if (!AppHelper.onOptionsItemSelected(item, preferences)) {\n            return false;\n        }\n        refresh();\n        return true;\n    }\n\n    public boolean onContextItemSelected(@NonNull MenuItem item) {\n        var info = selectedApplicationInfo;\n        if (info == null) {\n            return false;\n        }\n        int itemId = item.getItemId();\n        if (itemId == R.id.menu_launch) {\n            Intent launchIntent = AppHelper.getLaunchIntentForPackage(info.packageName, info.uid / App.PER_USER_RANGE);\n            if (launchIntent != null) {\n                ConfigManager.startActivityAsUserWithFeature(launchIntent, module.userId);\n            }\n        } else if (itemId == R.id.menu_compile_speed) {\n            CompileDialogFragment.speed(fragment.getChildFragmentManager(), info);\n        } else if (itemId == R.id.menu_other_app) {\n            var intent = new Intent(Intent.ACTION_SHOW_APP_INFO);\n            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, module.packageName);\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            ConfigManager.startActivityAsUserWithFeature(intent, module.userId);\n        } else if (itemId == R.id.menu_app_info) {\n            ConfigManager.startActivityAsUserWithFeature(new Intent(ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts(\"package\", info.packageName, null)), module.userId);\n        } else if (itemId == R.id.menu_force_stop) {\n            if (info.packageName.equals(\"system\")) {\n                new BlurBehindDialogBuilder(activity, R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons)\n                        .setTitle(R.string.reboot)\n                        .setPositiveButton(android.R.string.ok, (dialog, which) -> ConfigManager.reboot())\n                        .setNegativeButton(android.R.string.cancel, null)\n                        .show();\n            } else {\n                new BlurBehindDialogBuilder(activity, R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons)\n                        .setTitle(R.string.force_stop_dlg_title)\n                        .setMessage(R.string.force_stop_dlg_text)\n                        .setPositiveButton(android.R.string.ok, (dialog, which) -> ConfigManager.forceStopPackage(info.packageName, info.uid / 100000))\n                        .setNegativeButton(android.R.string.cancel, null)\n                        .show();\n            }\n        } else {\n            return false;\n        }\n        return true;\n    }\n\n    public void onPrepareOptionsMenu(@NonNull Menu menu) {\n        List<String> scopeList = module.getScopeList();\n        if (scopeList == null || scopeList.isEmpty()) {\n            menu.removeItem(R.id.use_recommended);\n        }\n        menu.findItem(R.id.item_filter_system).setChecked(preferences.getBoolean(\"filter_system_apps\", true));\n        menu.findItem(R.id.item_filter_games).setChecked(preferences.getBoolean(\"filter_games\", true));\n        menu.findItem(R.id.item_filter_modules).setChecked(preferences.getBoolean(\"filter_modules\", true));\n        menu.findItem(R.id.item_filter_denylist).setChecked(preferences.getBoolean(\"filter_denylist\", false));\n        switch (preferences.getInt(\"list_sort\", 0)) {\n            case 7 -> {\n                menu.findItem(R.id.item_sort_by_update_time).setChecked(true);\n                menu.findItem(R.id.reverse).setChecked(true);\n            }\n            case 6 -> menu.findItem(R.id.item_sort_by_update_time).setChecked(true);\n            case 5 -> {\n                menu.findItem(R.id.item_sort_by_install_time).setChecked(true);\n                menu.findItem(R.id.reverse).setChecked(true);\n            }\n            case 4 -> menu.findItem(R.id.item_sort_by_install_time).setChecked(true);\n            case 3 -> {\n                menu.findItem(R.id.item_sort_by_package_name).setChecked(true);\n                menu.findItem(R.id.reverse).setChecked(true);\n            }\n            case 2 -> menu.findItem(R.id.item_sort_by_package_name).setChecked(true);\n            case 1 -> {\n                menu.findItem(R.id.item_sort_by_name).setChecked(true);\n                menu.findItem(R.id.reverse).setChecked(true);\n            }\n            case 0 -> menu.findItem(R.id.item_sort_by_name).setChecked(true);\n        }\n        menu.findItem(R.id.auto_include).setChecked(ConfigManager.getAutoInclude(module.packageName));\n    }\n\n    @Override\n    public void onViewRecycled(@NonNull ViewHolder holder) {\n        if (holder.checkbox != null) {\n            holder.checkbox.setOnCheckedChangeListener(null);\n        }\n        super.onViewRecycled(holder);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {\n        AppInfo appInfo = showList.get(position);\n        boolean deny = denyList.contains(appInfo.packageName);\n        holder.root.setAlpha(!deny && enabled ? 1.0f : .5f);\n        boolean system = appInfo.packageName.equals(\"system\");\n        CharSequence appName;\n        int userId = appInfo.applicationInfo.uid / App.PER_USER_RANGE;\n        appName = system ? activity.getString(R.string.android_framework) : appInfo.label;\n        holder.appName.setText(appName);\n        GlideApp.with(holder.appIcon).load(appInfo.packageInfo).into(new CustomTarget<Drawable>() {\n            @Override\n            public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {\n                holder.appIcon.setImageDrawable(resource);\n            }\n\n            @Override\n            public void onLoadCleared(@Nullable Drawable placeholder) {\n\n            }\n\n            @Override\n            public void onLoadFailed(@Nullable Drawable errorDrawable) {\n                holder.appIcon.setImageDrawable(pm.getDefaultActivityIcon());\n            }\n        });\n        if (system) {\n            //noinspection SetTextI18n\n            holder.appPackageName.setText(\"system\");\n            holder.appVersionName.setVisibility(View.GONE);\n        } else {\n            holder.appVersionName.setVisibility(View.VISIBLE);\n            holder.appPackageName.setText(appInfo.packageName);\n        }\n        holder.appPackageName.setVisibility(View.VISIBLE);\n        holder.appVersionName.setText(activity.getString(R.string.app_version, appInfo.packageInfo.versionName));\n        var sb = new SpannableStringBuilder();\n        if (!recommendedList.isEmpty() && recommendedList.contains(appInfo.application)) {\n            String recommended = activity.getString(R.string.requested_by_module);\n            sb.append(recommended);\n            final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(activity.getTheme(), com.google.android.material.R.attr.colorPrimary));\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create(\"sans-serif-medium\", Typeface.NORMAL));\n                sb.setSpan(typefaceSpan, sb.length() - recommended.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            } else {\n                final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);\n                sb.setSpan(styleSpan, sb.length() - recommended.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            }\n            sb.setSpan(foregroundColorSpan, sb.length() - recommended.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n        }\n        if (deny) {\n            if (sb.length() != 0) sb.append(\"\\n\");\n            String denylist = activity.getString(R.string.deny_list_info);\n            sb.append(denylist);\n            final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(activity.getTheme(), com.google.android.material.R.attr.colorError));\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create(\"sans-serif-medium\", Typeface.NORMAL));\n                sb.setSpan(typefaceSpan, sb.length() - denylist.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            } else {\n                final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);\n                sb.setSpan(styleSpan, sb.length() - denylist.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            }\n            sb.setSpan(foregroundColorSpan, sb.length() - denylist.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n        }\n        if (sb.length() == 0) {\n            holder.hint.setVisibility(View.GONE);\n        } else {\n            holder.hint.setText(sb);\n            holder.hint.setVisibility(View.VISIBLE);\n        }\n\n        holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> {\n            activity.getMenuInflater().inflate(R.menu.menu_app_item, menu);\n            menu.setHeaderTitle(appName);\n            Intent launchIntent = AppHelper.getLaunchIntentForPackage(appInfo.packageName, userId);\n            if (launchIntent == null) {\n                menu.removeItem(R.id.menu_launch);\n            }\n            if (system) {\n                menu.findItem(R.id.menu_force_stop).setTitle(R.string.reboot);\n                menu.removeItem(R.id.menu_compile_speed);\n                menu.removeItem(R.id.menu_other_app);\n                menu.removeItem(R.id.menu_app_info);\n            }\n        });\n\n        holder.checkbox.setChecked(checkedList.contains(appInfo.application));\n\n        holder.checkbox.setOnCheckedChangeListener((v, isChecked) -> onCheckedChange(v, isChecked, appInfo));\n\n        holder.itemView.setOnClickListener(v -> {\n            if (enabled) holder.checkbox.toggle();\n        });\n        holder.itemView.setOnLongClickListener(v -> {\n            fragment.searchView.clearFocus();\n            selectedApplicationInfo = appInfo.applicationInfo;\n            return false;\n        });\n    }\n\n    @Override\n    public long getItemId(int position) {\n        PackageInfo info = showList.get(position).packageInfo;\n        return (info.packageName + \"!\" + info.applicationInfo.uid / App.PER_USER_RANGE).hashCode();\n    }\n\n    @Override\n    public Filter getFilter() {\n        return new ApplicationFilter();\n    }\n\n    @Override\n    public int getItemCount() {\n        return showList.size();\n    }\n\n    public void refresh() {\n        refresh(false);\n    }\n\n    public void refresh(boolean force) {\n        setLoaded(null, false);\n        enabled = moduleUtil.isModuleEnabled(module.packageName);\n        fragment.runAsync(() -> {\n            List<PackageInfo> appList = AppHelper.getAppList(force);\n            denyList = AppHelper.getDenyList(force);\n            var tmpRecList = new HashSet<ApplicationWithEquals>();\n            var tmpChkList = new HashSet<>(ConfigManager.getModuleScope(module.packageName));\n            final var tmpList = new ArrayList<AppInfo>();\n            final HashSet<ApplicationWithEquals> installedList = new HashSet<>();\n            List<String> scopeList = module.getScopeList();\n            boolean emptyCheckedList = tmpChkList.isEmpty();\n            appList.parallelStream().forEach(info -> {\n                int userId = info.applicationInfo.uid / App.PER_USER_RANGE;\n                String packageName = info.packageName;\n                if (packageName.equals(\"system\") && userId != 0 ||\n                        packageName.equals(module.packageName) ||\n                        packageName.equals(BuildConfig.APPLICATION_ID)) {\n                    return;\n                }\n\n                ApplicationWithEquals application = new ApplicationWithEquals(packageName, userId);\n\n                synchronized (installedList) {\n                    installedList.add(application);\n                }\n\n                if (userId != module.userId) {\n                    return;\n                }\n\n                if (scopeList != null && scopeList.contains(packageName)) {\n                    synchronized (tmpRecList) {\n                        tmpRecList.add(application);\n                    }\n                } else if (shouldHideApp(info, application, tmpChkList)) {\n                    return;\n                }\n\n                AppInfo appInfo = new AppInfo();\n                appInfo.packageInfo = info;\n                appInfo.label = AppHelper.getAppLabel(info, pm);\n                appInfo.application = application;\n                appInfo.packageName = info.packageName;\n                appInfo.applicationInfo = info.applicationInfo;\n                synchronized (tmpList) {\n                    tmpList.add(appInfo);\n                }\n            });\n            tmpChkList.retainAll(installedList);\n            checkedList = tmpChkList;\n            recommendedList = tmpRecList;\n            searchList = tmpList.parallelStream().sorted(this::sortApps).collect(Collectors.toList());\n\n            String queryStr = fragment.searchView != null ? fragment.searchView.getQuery().toString() : \"\";\n\n            fragment.runOnUiThread(() -> getFilter().filter(queryStr));\n        });\n    }\n\n    protected void onCheckedChange(CompoundButton buttonView, boolean isChecked, AppInfo appInfo) {\n        var tmpChkList = new HashSet<>(checkedList);\n        if (isChecked) {\n            tmpChkList.add(appInfo.application);\n        } else {\n            tmpChkList.remove(appInfo.application);\n        }\n        if (!ConfigManager.setModuleScope(module.packageName, module.legacy, tmpChkList)) {\n            fragment.showHint(R.string.failed_to_save_scope_list, true);\n            if (!isChecked) {\n                tmpChkList.add(appInfo.application);\n            } else {\n                tmpChkList.remove(appInfo.application);\n            }\n            buttonView.setChecked(!isChecked);\n        } else if (appInfo.packageName.equals(\"system\")) {\n            fragment.showHint(R.string.reboot_required, true, R.string.reboot, v -> ConfigManager.reboot());\n        } else if (denyList.contains(appInfo.packageName)) {\n            fragment.showHint(activity.getString(R.string.deny_list, appInfo.label), true);\n        }\n        checkedList = tmpChkList;\n    }\n\n    @Override\n    public boolean isLoaded() {\n        return isLoaded;\n    }\n\n    public static class ViewHolder extends RecyclerView.ViewHolder {\n        ConstraintLayout root;\n        ImageView appIcon;\n        TextView appName;\n        TextView appPackageName;\n        TextView appVersionName;\n        TextView hint;\n        MaterialCheckBox checkbox;\n\n        ViewHolder(ItemModuleBinding binding) {\n            super(binding.getRoot());\n            root = binding.itemRoot;\n            appIcon = binding.appIcon;\n            appName = binding.appName;\n            appPackageName = binding.appPackageName;\n            appVersionName = binding.appVersionName;\n            checkbox = binding.checkbox;\n            hint = binding.hint;\n            checkbox.setVisibility(View.VISIBLE);\n        }\n    }\n\n    private class ApplicationFilter extends Filter {\n\n        private boolean lowercaseContains(String s, String filter) {\n            return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);\n        }\n\n        @Override\n        protected FilterResults performFiltering(CharSequence constraint) {\n            FilterResults filterResults = new FilterResults();\n            List<AppInfo> filtered = new ArrayList<>();\n            String filter = constraint.toString().toLowerCase();\n            for (AppInfo info : searchList) {\n                if (lowercaseContains(info.label.toString(), filter)\n                        || lowercaseContains(info.packageName, filter)) {\n                    filtered.add(info);\n                }\n            }\n            filterResults.values = filtered;\n            filterResults.count = filtered.size();\n            return filterResults;\n        }\n\n        @Override\n        protected void publishResults(CharSequence constraint, FilterResults results) {\n            //noinspection unchecked\n            setLoaded((List<AppInfo>) results.values, true);\n        }\n    }\n\n    public SearchView.OnQueryTextListener getSearchListener() {\n        return new SearchView.OnQueryTextListener() {\n            @Override\n            public boolean onQueryTextSubmit(String query) {\n                getFilter().filter(query);\n                return true;\n            }\n\n            @Override\n            public boolean onQueryTextChange(String query) {\n                getFilter().filter(query);\n                return true;\n            }\n        };\n    }\n\n    public void onBackPressed() {\n        fragment.searchView.clearFocus();\n        if (isLoaded && enabled && checkedList.isEmpty()) {\n            var builder = new BlurBehindDialogBuilder(activity, R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons);\n            builder.setMessage(!recommendedList.isEmpty() ? R.string.no_scope_selected_has_recommended : R.string.no_scope_selected);\n            if (!recommendedList.isEmpty()) {\n                builder.setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended());\n            } else {\n                builder.setPositiveButton(android.R.string.cancel, null);\n            }\n            builder.setNegativeButton(!recommendedList.isEmpty() ? android.R.string.cancel : android.R.string.ok, (dialog, which) -> {\n                moduleUtil.setModuleEnabled(module.packageName, false);\n                Toast.makeText(activity, activity.getString(R.string.module_disabled_no_selection, module.getAppName()), Toast.LENGTH_LONG).show();\n                fragment.navigateUp();\n            });\n            builder.show();\n        } else {\n            fragment.navigateUp();\n        }\n    }\n\n    public static class AppInfo {\n        public PackageInfo packageInfo;\n        public ApplicationWithEquals application;\n        public ApplicationInfo applicationInfo;\n        public String packageName;\n        public CharSequence label = null;\n    }\n\n    public static class ApplicationWithEquals extends Application {\n        public ApplicationWithEquals(String packageName, int userId) {\n            this.packageName = packageName;\n            this.userId = userId;\n        }\n\n        public ApplicationWithEquals(Application application) {\n            packageName = application.packageName;\n            userId = application.userId;\n        }\n\n        @Override\n        public boolean equals(@Nullable Object obj) {\n            if (!(obj instanceof Application)) {\n                return false;\n            }\n            return packageName.equals(((Application) obj).packageName) && userId == ((Application) obj).userId;\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(packageName, userId);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/receivers/LSPManagerServiceHolder.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.receivers;\n\nimport android.os.IBinder;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.system.Os;\n\nimport org.lsposed.lspd.ILSPManagerService;\n\npublic class LSPManagerServiceHolder implements IBinder.DeathRecipient {\n    private static LSPManagerServiceHolder holder = null;\n    private static ILSPManagerService service = null;\n\n    public static void init(IBinder binder) {\n        if (holder == null) {\n            holder = new LSPManagerServiceHolder(binder);\n        }\n    }\n\n    public static ILSPManagerService getService() {\n        return service;\n    }\n\n    private LSPManagerServiceHolder(IBinder binder) {\n        linkToDeath(binder);\n        service = ILSPManagerService.Stub.asInterface(binder);\n    }\n\n    private void linkToDeath(IBinder binder) {\n        try {\n            binder.linkToDeath(this, 0);\n        } catch (RemoteException e) {\n            binderDied();\n        }\n    }\n\n    @Override\n    public void binderDied() {\n        System.exit(0);\n        Process.killProcess(Os.getpid());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/repo/RepoLoader.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.repo;\n\nimport android.content.res.Resources;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.google.gson.Gson;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.repo.model.OnlineModule;\nimport org.lsposed.manager.repo.model.Release;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport okhttp3.Call;\nimport okhttp3.Callback;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic class RepoLoader {\n    private static RepoLoader instance = null;\n    private Map<String, OnlineModule> onlineModules = new HashMap<>();\n    private Map<String, ModuleVersion> latestVersion = new ConcurrentHashMap<>();\n\n    public static class ModuleVersion {\n        public String versionName;\n        public long versionCode;\n\n        private ModuleVersion(long versionCode, String versionName) {\n            this.versionName = versionName;\n            this.versionCode = versionCode;\n        }\n\n        public boolean upgradable(long versionCode, String versionName) {\n            return this.versionCode > versionCode || (this.versionCode == versionCode && !versionName.replace(' ', '_').equals(this.versionName));\n        }\n\n    }\n\n    private final Path repoFile = Paths.get(App.getInstance().getFilesDir().getAbsolutePath(), \"repo.json\");\n    private final Set<RepoListener> listeners = ConcurrentHashMap.newKeySet();\n    private boolean repoLoaded = false;\n    private static final String originRepoUrl = \"https://modules.lsposed.org/\";\n    private static final String backupRepoUrl = \"https://modules-blogcdn.lsposed.org/\";\n\n    private static final String secondBackupRepoUrl = \"https://modules-cloudflare.lsposed.org/\";\n    private static String repoUrl = originRepoUrl;\n    private final Resources resources = App.getInstance().getResources();\n    private final String[] channels = resources.getStringArray(R.array.update_channel_values);\n\n    public boolean isRepoLoaded() {\n        return repoLoaded;\n    }\n\n    public static synchronized RepoLoader getInstance() {\n        if (instance == null) {\n            instance = new RepoLoader();\n            App.getExecutorService().submit(() -> instance.loadLocalData(true));\n        }\n        return instance;\n    }\n\n    synchronized public void loadRemoteData() {\n        repoLoaded = false;\n        try {\n            try (var response = App.getOkHttpClient().newCall(new Request.Builder().url(repoUrl + \"modules.json\").build()).execute()) {\n\n                if (response.isSuccessful()) {\n                    ResponseBody body = response.body();\n                    if (body != null) {\n                        try {\n                            String bodyString = body.string();\n                            Files.write(repoFile, bodyString.getBytes(StandardCharsets.UTF_8));\n                            loadLocalData(false);\n                        } catch (Throwable t) {\n                            Log.e(App.TAG, Log.getStackTraceString(t));\n                            for (RepoListener listener : listeners) {\n                                listener.onThrowable(t);\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (Throwable e) {\n            Log.e(App.TAG, \"load remote data\", e);\n            for (RepoListener listener : listeners) {\n                listener.onThrowable(e);\n            }\n            if (repoUrl.equals(originRepoUrl)) {\n                repoUrl = backupRepoUrl;\n                loadRemoteData();\n            } else if (repoUrl.equals(backupRepoUrl)) {\n                repoUrl = secondBackupRepoUrl;\n                loadRemoteData();\n            }\n        }\n    }\n\n    synchronized public void loadLocalData(boolean updateRemoteRepo) {\n        repoLoaded = false;\n        try {\n            if (Files.notExists(repoFile)) {\n                loadRemoteData();\n                updateRemoteRepo = false;\n            }\n            byte[] encoded = Files.readAllBytes(repoFile);\n            String bodyString = new String(encoded, StandardCharsets.UTF_8);\n            Gson gson = new Gson();\n            Map<String, OnlineModule> modules = new HashMap<>();\n            OnlineModule[] repoModules = gson.fromJson(bodyString, OnlineModule[].class);\n            Arrays.stream(repoModules).forEach(onlineModule -> modules.put(onlineModule.getName(), onlineModule));\n            var channel = App.getPreferences().getString(\"update_channel\", channels[0]);\n            updateLatestVersion(repoModules, channel);\n            onlineModules = modules;\n        } catch (Throwable t) {\n            Log.e(App.TAG, Log.getStackTraceString(t));\n            for (RepoListener listener : listeners) {\n                listener.onThrowable(t);\n            }\n        } finally {\n            repoLoaded = true;\n            for (RepoListener listener : listeners) {\n                listener.onRepoLoaded();\n            }\n            if (updateRemoteRepo) loadRemoteData();\n        }\n    }\n\n    synchronized private void updateLatestVersion(OnlineModule[] onlineModules, String channel) {\n        repoLoaded = false;\n        Map<String, ModuleVersion> versions = new ConcurrentHashMap<>();\n        for (var module : onlineModules) {\n            String release = module.getLatestRelease();\n            if (channel.equals(channels[1]) && module.getLatestBetaRelease() != null && !module.getLatestBetaRelease().isEmpty()) {\n                release = module.getLatestBetaRelease();\n            } else if (channel.equals(channels[2])) {\n                if (module.getLatestSnapshotRelease() != null && !module.getLatestSnapshotRelease().isEmpty())\n                    release = module.getLatestSnapshotRelease();\n                else if (module.getLatestBetaRelease() != null && !module.getLatestBetaRelease().isEmpty())\n                    release = module.getLatestBetaRelease();\n            }\n            if (release == null || release.isEmpty()) continue;\n            var splits = release.split(\"-\", 2);\n            if (splits.length < 2) continue;\n            long verCode;\n            String verName;\n            try {\n                verCode = Long.parseLong(splits[0]);\n                verName = splits[1];\n            } catch (NumberFormatException ignored) {\n                continue;\n            }\n            String pkgName = module.getName();\n            versions.put(pkgName, new ModuleVersion(verCode, verName));\n        }\n        latestVersion = versions;\n        repoLoaded = true;\n        for (RepoListener listener : listeners) {\n            listener.onRepoLoaded();\n        }\n    }\n\n    public void updateLatestVersion(String channel) {\n        if (repoLoaded)\n            updateLatestVersion(onlineModules.keySet().parallelStream().map(onlineModules::get).toArray(OnlineModule[]::new), channel);\n    }\n\n    @Nullable\n    public ModuleVersion getModuleLatestVersion(String packageName) {\n        return repoLoaded ? latestVersion.getOrDefault(packageName, null) : null;\n    }\n\n    @Nullable\n    public List<Release> getReleases(String packageName) {\n        var channel = App.getPreferences().getString(\"update_channel\", channels[0]);\n        List<Release> releases = new ArrayList<>();\n        if (repoLoaded) {\n            var module = onlineModules.get(packageName);\n            if (module != null) {\n                releases = module.getReleases();\n                if (!module.releasesLoaded) {\n                    if (channel.equals(channels[1]) && !(module.getBetaReleases() != null && module.getBetaReleases().isEmpty())) {\n                        releases = module.getBetaReleases();\n                    } else if (channel.equals(channels[2]))\n                        if (!(module.getSnapshotReleases() != null && module.getSnapshotReleases().isEmpty()))\n                            releases = module.getSnapshotReleases();\n                        else if (!(module.getBetaReleases() != null && module.getBetaReleases().isEmpty()))\n                            releases = module.getBetaReleases();\n                }\n            }\n        }\n        return releases;\n    }\n\n    @Nullable\n    public String getLatestReleaseTime(String packageName, String channel) {\n        String releaseTime = null;\n        if (repoLoaded) {\n            var module = onlineModules.get(packageName);\n            if (module != null) {\n                releaseTime = module.getLatestReleaseTime();\n                if (channel.equals(channels[1]) && module.getLatestBetaReleaseTime() != null) {\n                    releaseTime = module.getLatestBetaReleaseTime();\n                } else if (channel.equals(channels[2]))\n                    if (module.getLatestSnapshotReleaseTime() != null)\n                        releaseTime = module.getLatestSnapshotReleaseTime();\n                    else if (module.getLatestBetaReleaseTime() != null)\n                        releaseTime = module.getLatestBetaReleaseTime();\n            }\n        }\n        return releaseTime;\n    }\n\n    public void loadRemoteReleases(String packageName) {\n        App.getOkHttpClient().newCall(new Request.Builder().url(String.format(repoUrl + \"module/%s.json\", packageName)).build()).enqueue(new Callback() {\n            @Override\n            public void onFailure(@NonNull Call call, @NonNull IOException e) {\n                Log.e(App.TAG, call.request().url() + e.getMessage());\n                if (repoUrl.equals(originRepoUrl)) {\n                    repoUrl = backupRepoUrl;\n                    loadRemoteReleases(packageName);\n                } else if (repoUrl.equals(backupRepoUrl)) {\n                    repoUrl = secondBackupRepoUrl;\n                    loadRemoteReleases(packageName);\n                } else {\n                    for (RepoListener listener : listeners) {\n                        listener.onThrowable(e);\n                    }\n                }\n            }\n\n            @Override\n            public void onResponse(@NonNull Call call, @NonNull Response response) {\n                if (response.isSuccessful()) {\n                    ResponseBody body = response.body();\n                    if (body != null) {\n                        try {\n                            String bodyString = body.string();\n                            Gson gson = new Gson();\n                            OnlineModule module = gson.fromJson(bodyString, OnlineModule.class);\n                            module.releasesLoaded = true;\n                            onlineModules.replace(packageName, module);\n                            for (RepoListener listener : listeners) {\n                                listener.onModuleReleasesLoaded(module);\n                            }\n                        } catch (Throwable t) {\n                            Log.e(App.TAG, Log.getStackTraceString(t));\n                            for (RepoListener listener : listeners) {\n                                listener.onThrowable(t);\n                            }\n                        }\n                    }\n                }\n            }\n        });\n    }\n\n    public void addListener(RepoListener listener) {\n        listeners.add(listener);\n    }\n\n    public void removeListener(RepoListener listener) {\n        listeners.remove(listener);\n    }\n\n    @Nullable\n    public OnlineModule getOnlineModule(String packageName) {\n        return repoLoaded && packageName != null ? onlineModules.get(packageName) : null;\n    }\n\n    @Nullable\n    public Collection<OnlineModule> getOnlineModules() {\n        return repoLoaded ? onlineModules.values() : null;\n    }\n\n    public interface RepoListener {\n        default void onRepoLoaded() {\n        }\n\n        default void onModuleReleasesLoaded(OnlineModule module) {\n        }\n\n        default void onThrowable(Throwable t) {\n            Log.e(App.TAG, \"load repo failed\", t);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/repo/model/Collaborator.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.repo.model;\n\nimport androidx.annotation.Nullable;\n\nimport com.google.gson.annotations.Expose;\nimport com.google.gson.annotations.SerializedName;\n\npublic class Collaborator {\n\n    @SerializedName(\"login\")\n    @Expose\n    private String login;\n    @SerializedName(\"name\")\n    @Expose\n    private String name;\n\n    @Nullable\n    public String getLogin() {\n        return login;\n    }\n\n    public void setLogin(String login) {\n        this.login = login;\n    }\n\n    @Nullable\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/repo/model/OnlineModule.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.repo.model;\n\nimport androidx.annotation.Nullable;\n\nimport com.google.gson.annotations.Expose;\nimport com.google.gson.annotations.SerializedName;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class OnlineModule {\n\n    @SerializedName(\"name\")\n    @Expose\n    private String name;\n    @SerializedName(\"description\")\n    @Expose\n    private String description;\n    @SerializedName(\"url\")\n    @Expose\n    private String url;\n    @SerializedName(\"homepageUrl\")\n    @Expose\n    private String homepageUrl;\n    @SerializedName(\"collaborators\")\n    @Expose\n    private List<Collaborator> collaborators = new ArrayList<>();\n    @SerializedName(\"latestRelease\")\n    @Expose\n    private String latestRelease;\n    @SerializedName(\"latestReleaseTime\")\n    @Expose\n    private String latestReleaseTime;\n    @SerializedName(\"latestBetaRelease\")\n    @Expose\n    private String latestBetaRelease;\n    @SerializedName(\"latestBetaReleaseTime\")\n    @Expose\n    private String latestBetaReleaseTime;\n    @SerializedName(\"latestSnapshotRelease\")\n    @Expose\n    private String latestSnapshotRelease;\n    @SerializedName(\"latestSnapshotReleaseTime\")\n    @Expose\n    private String latestSnapshotReleaseTime;\n    @SerializedName(\"releases\")\n    @Expose\n    private List<Release> releases = new ArrayList<>();\n    @SerializedName(\"betaReleases\")\n    @Expose\n    private final List<Release> betaReleases = new ArrayList<>();\n    @SerializedName(\"snapshotReleases\")\n    @Expose\n    private final List<Release> snapshotReleases = new ArrayList<>();\n    @SerializedName(\"readme\")\n    @Expose\n    private String readme;\n    @SerializedName(\"readmeHTML\")\n    @Expose\n    private String readmeHTML;\n    @SerializedName(\"summary\")\n    @Expose\n    private String summary;\n    @SerializedName(\"scope\")\n    @Expose\n    private List<String> scope = new ArrayList<>();\n    @SerializedName(\"sourceUrl\")\n    @Expose\n    private String sourceUrl;\n    @SerializedName(\"hide\")\n    @Expose\n    private Boolean hide;\n    @SerializedName(\"additionalAuthors\")\n    @Expose\n    private List<Object> additionalAuthors = null;\n    @SerializedName(\"updatedAt\")\n    @Expose\n    private String updatedAt;\n    @SerializedName(\"createdAt\")\n    @Expose\n    private String createdAt;\n    @SerializedName(\"stargazerCount\")\n    @Expose\n    private Integer stargazerCount;\n    public boolean releasesLoaded = false;\n\n    @Nullable\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Nullable\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    @Nullable\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    @Nullable\n    public String getHomepageUrl() {\n        return homepageUrl;\n    }\n\n    public void setHomepageUrl(String homepageUrl) {\n        this.homepageUrl = homepageUrl;\n    }\n\n    @Nullable\n    public List<Collaborator> getCollaborators() {\n        return collaborators;\n    }\n\n    public void setCollaborators(List<Collaborator> collaborators) {\n        this.collaborators = collaborators;\n    }\n\n    @Nullable\n    public List<Release> getReleases() {\n        return releases;\n    }\n\n    @Nullable\n    public String getLatestReleaseTime() {\n        return latestReleaseTime;\n    }\n\n    public void setReleases(List<Release> releases) {\n        this.releases = releases;\n    }\n\n    @Nullable\n    public String getReadme() {\n        return readme;\n    }\n\n    public void setReadme(String readme) {\n        this.readme = readme;\n    }\n\n    @Nullable\n    public String getReadmeHTML() {\n        return readmeHTML;\n    }\n\n    public void setReadmeHTML(String readmeHTML) {\n        this.readmeHTML = readmeHTML;\n    }\n\n    @Nullable\n    public String getSummary() {\n        return summary;\n    }\n\n    public void setSummary(String summary) {\n        this.summary = summary;\n    }\n\n    @Nullable\n    public List<String> getScope() {\n        return scope;\n    }\n\n    public void setScope(List<String> scope) {\n        this.scope = scope;\n    }\n\n    @Nullable\n    public String getSourceUrl() {\n        return sourceUrl;\n    }\n\n    public void setSourceUrl(String sourceUrl) {\n        this.sourceUrl = sourceUrl;\n    }\n\n    public Boolean isHide() {\n        return hide;\n    }\n\n    public void setHide(Boolean hide) {\n        this.hide = hide;\n    }\n\n    @Nullable\n    public List<Object> getAdditionalAuthors() {\n        return additionalAuthors;\n    }\n\n    public void setAdditionalAuthors(List<Object> additionalAuthors) {\n        this.additionalAuthors = additionalAuthors;\n    }\n\n    @Nullable\n    public String getUpdatedAt() {\n        return updatedAt;\n    }\n\n    public void setUpdatedAt(String updatedAt) {\n        this.updatedAt = updatedAt;\n    }\n\n    @Nullable\n    public String getCreatedAt() {\n        return createdAt;\n    }\n\n    public void setCreatedAt(String createdAt) {\n        this.createdAt = createdAt;\n    }\n\n    @Nullable\n    public Integer getStargazerCount() {\n        return stargazerCount;\n    }\n\n    public void setStargazerCount(Integer stargazerCount) {\n        this.stargazerCount = stargazerCount;\n    }\n\n    @Nullable\n    public String getLatestRelease() {\n        return latestRelease;\n    }\n\n    public void setLatestRelease(String latestRelease) {\n        this.latestRelease = latestRelease;\n    }\n\n    @Nullable\n    public String getLatestBetaRelease() {\n        return latestBetaRelease;\n    }\n\n    @Nullable\n    public String getLatestBetaReleaseTime() {\n        return latestBetaReleaseTime;\n    }\n\n    @Nullable\n    public String getLatestSnapshotRelease() {\n        return latestSnapshotRelease;\n    }\n\n    @Nullable\n    public String getLatestSnapshotReleaseTime() {\n        return latestSnapshotReleaseTime;\n    }\n\n    @Nullable\n    public List<Release> getBetaReleases() {\n        return betaReleases;\n    }\n\n    @Nullable\n    public List<Release> getSnapshotReleases() {\n        return snapshotReleases;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/repo/model/Release.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.repo.model;\n\nimport androidx.annotation.Nullable;\n\nimport com.google.gson.annotations.Expose;\nimport com.google.gson.annotations.SerializedName;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Release {\n\n    @SerializedName(\"name\")\n    @Expose\n    private String name;\n    @SerializedName(\"url\")\n    @Expose\n    private String url;\n    @SerializedName(\"description\")\n    @Expose\n    private String description;\n    @SerializedName(\"descriptionHTML\")\n    @Expose\n    private String descriptionHTML;\n    @SerializedName(\"createdAt\")\n    @Expose\n    private String createdAt;\n    @SerializedName(\"publishedAt\")\n    @Expose\n    private String publishedAt;\n    @SerializedName(\"updatedAt\")\n    @Expose\n    private String updatedAt;\n    @SerializedName(\"tagName\")\n    @Expose\n    private String tagName;\n    @SerializedName(\"isPrerelease\")\n    @Expose\n    private Boolean isPrerelease;\n    @SerializedName(\"releaseAssets\")\n    @Expose\n    private List<ReleaseAsset> releaseAssets = new ArrayList<>();\n\n    @Nullable\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Nullable\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    @Nullable\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    @Nullable\n    public String getDescriptionHTML() {\n        return descriptionHTML;\n    }\n\n    public void setDescriptionHTML(String descriptionHTML) {\n        this.descriptionHTML = descriptionHTML;\n    }\n\n    @Nullable\n    public String getCreatedAt() {\n        return createdAt;\n    }\n\n    public void setCreatedAt(String createdAt) {\n        this.createdAt = createdAt;\n    }\n\n    @Nullable\n    public String getPublishedAt() {\n        return publishedAt;\n    }\n\n    public void setPublishedAt(String publishedAt) {\n        this.publishedAt = publishedAt;\n    }\n\n    @Nullable\n    public String getUpdatedAt() {\n        return updatedAt;\n    }\n\n    public void setUpdatedAt(String updatedAt) {\n        this.updatedAt = updatedAt;\n    }\n\n    @Nullable\n    public String getTagName() {\n        return tagName;\n    }\n\n    public void setTagName(String tagName) {\n        this.tagName = tagName;\n    }\n\n    @Nullable\n    public Boolean getIsPrerelease() {\n        return isPrerelease;\n    }\n\n    public void setIsPrerelease(Boolean isPrerelease) {\n        this.isPrerelease = isPrerelease;\n    }\n\n    @Nullable\n    public List<ReleaseAsset> getReleaseAssets() {\n        return releaseAssets;\n    }\n\n    public void setReleaseAssets(List<ReleaseAsset> releaseAssets) {\n        this.releaseAssets = releaseAssets;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/repo/model/ReleaseAsset.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.repo.model;\n\nimport androidx.annotation.Nullable;\n\nimport com.google.gson.annotations.Expose;\nimport com.google.gson.annotations.SerializedName;\n\npublic class ReleaseAsset {\n\n    @SerializedName(\"name\")\n    @Expose\n    private String name;\n    @SerializedName(\"contentType\")\n    @Expose\n    private String contentType;\n    @SerializedName(\"downloadUrl\")\n    @Expose\n    private String downloadUrl;\n    @SerializedName(\"downloadCount\")\n    @Expose\n    private int downloadCount = 0;\n    @SerializedName(\"size\")\n    @Expose\n    private int size = 0;\n\n    @Nullable\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Nullable\n    public String getContentType() {\n        return contentType;\n    }\n\n    public void setContentType(String contentType) {\n        this.contentType = contentType;\n    }\n\n    @Nullable\n    public String getDownloadUrl() {\n        return downloadUrl;\n    }\n\n    public void setDownloadUrl(String downloadUrl) {\n        this.downloadUrl = downloadUrl;\n    }\n\n    public int getDownloadCount() {\n        return downloadCount;\n    }\n\n    public void setDownloadCount(int downloadCount) {\n        this.downloadCount = downloadCount;\n    }\n\n    public int getSize() {\n        return size;\n    }\n\n    public void setSize(int size) {\n        this.size = size;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/activity/MainActivity.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.activity;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\n\nimport androidx.annotation.NonNull;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavOptions;\nimport androidx.navigation.Navigation;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.navigation.ui.NavigationUI;\n\nimport com.google.android.material.navigation.NavigationBarView;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.ActivityMainBinding;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.ui.activity.base.BaseActivity;\nimport org.lsposed.manager.util.ModuleUtil;\nimport org.lsposed.manager.util.ShortcutUtil;\nimport org.lsposed.manager.util.UpdateUtil;\n\nimport java.util.HashSet;\nimport java.util.Objects;\n\nimport rikka.core.util.ResourceUtils;\n\npublic class MainActivity extends BaseActivity implements RepoLoader.RepoListener, ModuleUtil.ModuleListener {\n    private static final String KEY_PREFIX = MainActivity.class.getName() + '.';\n    private static final String EXTRA_SAVED_INSTANCE_STATE = KEY_PREFIX + \"SAVED_INSTANCE_STATE\";\n\n    private static final RepoLoader repoLoader = RepoLoader.getInstance();\n    private static final ModuleUtil moduleUtil = ModuleUtil.getInstance();\n\n    private boolean restarting;\n    private ActivityMainBinding binding;\n\n    @NonNull\n    public static Intent newIntent(@NonNull Context context) {\n        return new Intent(context, MainActivity.class);\n    }\n\n    @NonNull\n    private static Intent newIntent(@NonNull Bundle savedInstanceState, @NonNull Context context) {\n        return newIntent(context)\n                .putExtra(EXTRA_SAVED_INSTANCE_STATE, savedInstanceState);\n    }\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        if (savedInstanceState == null) {\n            savedInstanceState = getIntent().getBundleExtra(EXTRA_SAVED_INSTANCE_STATE);\n        }\n        super.onCreate(savedInstanceState);\n\n        binding = ActivityMainBinding.inflate(getLayoutInflater());\n        setContentView(binding.getRoot());\n\n        repoLoader.addListener(this);\n        moduleUtil.addListener(this);\n\n        onModulesReloaded();\n\n        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);\n        if (navHostFragment == null) {\n            return;\n        }\n\n        NavController navController = navHostFragment.getNavController();\n        var nav = (NavigationBarView) binding.nav;\n        NavigationUI.setupWithNavController(nav, navController);\n\n        handleIntent(getIntent());\n    }\n\n    @Override\n    protected void onNewIntent(Intent intent) {\n        super.onNewIntent(intent);\n        handleIntent(intent);\n    }\n\n    private void handleIntent(Intent intent) {\n        if (intent == null) {\n            return;\n        }\n        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);\n        if (navHostFragment == null) {\n            return;\n        }\n        NavController navController = navHostFragment.getNavController();\n        var nav = (NavigationBarView) binding.nav;\n        if (intent.getAction() != null && intent.getAction().equals(\"android.intent.action.APPLICATION_PREFERENCES\")) {\n            nav.setSelectedItemId(R.id.settings_fragment);\n        } else if (ConfigManager.isBinderAlive()) {\n            if (!TextUtils.isEmpty(intent.getDataString())) {\n                switch (intent.getDataString()) {\n                    case \"modules\" -> nav.setSelectedItemId(R.id.modules_nav);\n                    case \"logs\" -> nav.setSelectedItemId(R.id.logs_fragment);\n                    case \"repo\" -> {\n                        if (ConfigManager.isMagiskInstalled()) {\n                            nav.setSelectedItemId(R.id.repo_nav);\n                        }\n                    }\n                    case \"settings\" -> nav.setSelectedItemId(R.id.settings_fragment);\n                    default -> {\n                        var data = intent.getData();\n                        if (data != null && Objects.equals(data.getScheme(), \"module\")) {\n                            navController.navigate(\n                                    new Uri.Builder().scheme(\"lsposed\").authority(\"module\").appendQueryParameter(\"modulePackageName\", data.getHost()).appendQueryParameter(\"moduleUserId\", String.valueOf(data.getPort())).build(),\n                                    new NavOptions.Builder().setEnterAnim(R.anim.fragment_enter).setExitAnim(R.anim.fragment_exit).setPopEnterAnim(R.anim.fragment_enter_pop).setPopExitAnim(R.anim.fragment_exit_pop).setLaunchSingleTop(true).setPopUpTo(navController.getGraph().getStartDestinationId(), false, true).build());\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean onSupportNavigateUp() {\n        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);\n        return navController.navigateUp() || super.onSupportNavigateUp();\n    }\n\n    public void restart() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || App.isParasitic) {\n            recreate();\n        } else {\n            try {\n                Bundle savedInstanceState = new Bundle();\n                onSaveInstanceState(savedInstanceState);\n                finish();\n                startActivity(newIntent(savedInstanceState, this));\n                overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);\n                restarting = true;\n            } catch (Throwable e) {\n                recreate();\n            }\n        }\n    }\n\n    @Override\n    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {\n        return restarting || super.dispatchKeyEvent(event);\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    @Override\n    public boolean dispatchKeyShortcutEvent(@NonNull KeyEvent event) {\n        return restarting || super.dispatchKeyShortcutEvent(event);\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {\n        return restarting || super.dispatchTouchEvent(event);\n    }\n\n    @Override\n    public boolean dispatchTrackballEvent(@NonNull MotionEvent event) {\n        return restarting || super.dispatchTrackballEvent(event);\n    }\n\n    @Override\n    public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) {\n        return restarting || super.dispatchGenericMotionEvent(event);\n    }\n\n\n    @Override\n    public void onRepoLoaded() {\n        final int[] count = new int[]{0};\n        HashSet<String> processedModules = new HashSet<>();\n        var modules = moduleUtil.getModules();\n        if (modules == null) return;\n        modules.forEach((k, v) -> {\n                    if (!processedModules.contains(k.first)) {\n                        var ver = repoLoader.getModuleLatestVersion(k.first);\n                        if (ver != null && ver.upgradable(v.versionCode, v.versionName)) {\n                            ++count[0];\n                        }\n                        processedModules.add(k.first);\n                    }\n                }\n        );\n        runOnUiThread(() -> {\n            if (count[0] > 0 && binding != null) {\n                var nav = (NavigationBarView) binding.nav;\n                var badge = nav.getOrCreateBadge(R.id.repo_nav);\n                badge.setVisible(true);\n                badge.setNumber(count[0]);\n            } else {\n                onThrowable(null);\n            }\n        });\n    }\n\n    @Override\n    public void onThrowable(Throwable t) {\n        runOnUiThread(() -> {\n            if (binding != null) {\n                var nav = (NavigationBarView) binding.nav;\n                var badge = nav.getOrCreateBadge(R.id.repo_nav);\n                badge.setVisible(false);\n            }\n        });\n    }\n\n    @Override\n    public void onModulesReloaded() {\n        onRepoLoaded();\n        setModulesSummary(moduleUtil.getEnabledModulesCount());\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        if (ConfigManager.isBinderAlive()) {\n            setModulesSummary(moduleUtil.getEnabledModulesCount());\n        } else setModulesSummary(0);\n        if (binding != null) {\n            var nav = (NavigationBarView) binding.nav;\n            if (UpdateUtil.needUpdate()) {\n                var badge = nav.getOrCreateBadge(R.id.main_fragment);\n                badge.setVisible(true);\n            }\n\n            if (!ConfigManager.isBinderAlive()) {\n                nav.getMenu().removeItem(R.id.logs_fragment);\n                nav.getMenu().removeItem(R.id.modules_nav);\n                if (!ConfigManager.isMagiskInstalled()) {\n                    nav.getMenu().removeItem(R.id.repo_nav);\n                }\n            }\n        }\n        if (App.isParasitic) {\n            var updateShortcut = ShortcutUtil.updateShortcut();\n            Log.d(App.TAG, \"update shortcut success = \" + updateShortcut);\n        }\n    }\n\n    private void setModulesSummary(int moduleCount) {\n        runOnUiThread(() -> {\n            if (binding != null) {\n                var nav = (NavigationBarView) binding.nav;\n                var badge = nav.getOrCreateBadge(R.id.modules_nav);\n                badge.setBackgroundColor(ResourceUtils.resolveColor(getTheme(), com.google.android.material.R.attr.colorPrimary));\n                badge.setBadgeTextColor(ResourceUtils.resolveColor(getTheme(), com.google.android.material.R.attr.colorOnPrimary));\n                if (moduleCount > 0) {\n                    badge.setVisible(true);\n                    badge.setNumber(moduleCount);\n                } else {\n                    badge.setVisible(false);\n                }\n            }\n        });\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        repoLoader.removeListener(this);\n        moduleUtil.removeListener(this);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/activity/base/BaseActivity.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.activity.base;\n\nimport android.app.ActivityManager;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.drawable.AdaptiveIconDrawable;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.os.Bundle;\nimport android.view.Window;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.util.ThemeUtil;\n\nimport rikka.material.app.MaterialActivity;\n\npublic class BaseActivity extends MaterialActivity {\n    private static Bitmap icon = null;\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        setTheme(R.style.AppTheme);\n        super.onCreate(savedInstanceState);\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        if (!App.isParasitic) return;\n        for (var task : getSystemService(ActivityManager.class).getAppTasks()) {\n            task.setExcludeFromRecents(false);\n        }\n        if (icon == null) {\n            var drawable = getApplicationInfo().loadIcon(getPackageManager());\n            if (drawable instanceof BitmapDrawable) {\n                icon = ((BitmapDrawable) drawable).getBitmap();\n            } else if (drawable instanceof AdaptiveIconDrawable) {\n                icon = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);\n                final Canvas canvas = new Canvas(icon);\n                drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n                drawable.draw(canvas);\n            }\n        }\n        setTaskDescription(new ActivityManager.TaskDescription(getTitle().toString(), icon, getColor(R.color.ic_launcher_background)));\n    }\n\n    @Override\n    public void onApplyUserThemeResource(@NonNull Resources.Theme theme, boolean isDecorView) {\n        if (!ThemeUtil.isSystemAccent()) {\n            theme.applyStyle(ThemeUtil.getColorThemeStyleRes(), true);\n        }\n        theme.applyStyle(ThemeUtil.getNightThemeStyleRes(this), true);\n        theme.applyStyle(rikka.material.preference.R.style.ThemeOverlay_Rikka_Material3_Preference, true);\n    }\n\n    @Override\n    public String computeUserThemeKey() {\n        return ThemeUtil.getColorTheme() + ThemeUtil.getNightTheme(this);\n    }\n\n    @Override\n    public void onApplyTranslucentSystemBars() {\n        super.onApplyTranslucentSystemBars();\n        Window window = getWindow();\n        window.setStatusBarColor(Color.TRANSPARENT);\n        window.setNavigationBarColor(Color.TRANSPARENT);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/dialog/BlurBehindDialogBuilder.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.dialog;\n\nimport android.animation.ValueAnimator;\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.Log;\nimport android.view.SurfaceControl;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.view.animation.DecelerateInterpolator;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AlertDialog;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\n\nimport org.lsposed.manager.App;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Consumer;\n\n@SuppressWarnings({\"JavaReflectionMemberAccess\", \"ConstantConditions\"})\npublic class BlurBehindDialogBuilder extends MaterialAlertDialogBuilder {\n    private static final boolean supportBlur = getSystemProperty(\"ro.surface_flinger.supports_background_blur\", false) && !getSystemProperty(\"persist.sys.sf.disable_blurs\", false);\n\n    public BlurBehindDialogBuilder(@NonNull Context context) {\n        super(context);\n    }\n\n    public BlurBehindDialogBuilder(@NonNull Context context, int overrideThemeResId) {\n        super(context, overrideThemeResId);\n    }\n\n    @NonNull\n    @Override\n    public AlertDialog create() {\n        AlertDialog dialog = super.create();\n        setupWindowBlurListener(dialog);\n        return dialog;\n    }\n\n    private void setupWindowBlurListener(AlertDialog dialog) {\n        var window = dialog.getWindow();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n            window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);\n            Consumer<Boolean> windowBlurEnabledListener = enabled -> updateWindowForBlurs(window, enabled);\n            window.getDecorView().addOnAttachStateChangeListener(\n                    new View.OnAttachStateChangeListener() {\n                        @Override\n                        public void onViewAttachedToWindow(@NonNull View v) {\n                            window.getWindowManager().addCrossWindowBlurEnabledListener(\n                                    windowBlurEnabledListener);\n                        }\n\n                        @Override\n                        public void onViewDetachedFromWindow(@NonNull View v) {\n                            window.getWindowManager().removeCrossWindowBlurEnabledListener(\n                                    windowBlurEnabledListener);\n                        }\n                    });\n        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n            dialog.setOnShowListener(d -> updateWindowForBlurs(window, supportBlur));\n        }\n    }\n\n    private void updateWindowForBlurs(Window window, boolean blursEnabled) {\n        float mDimAmountWithBlur = 0.1f;\n        float mDimAmountNoBlur = 0.32f;\n        window.setDimAmount(blursEnabled ?\n                mDimAmountWithBlur : mDimAmountNoBlur);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n            window.getAttributes().setBlurBehindRadius(20);\n            window.setAttributes(window.getAttributes());\n        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n            if (blursEnabled) {\n                View view = window.getDecorView();\n                ValueAnimator animator = ValueAnimator.ofInt(1, 53);\n                animator.setInterpolator(new DecelerateInterpolator());\n                try {\n                    Object viewRootImpl = view.getClass().getMethod(\"getViewRootImpl\").invoke(view);\n                    if (viewRootImpl == null) {\n                        return;\n                    }\n                    SurfaceControl surfaceControl = (SurfaceControl) viewRootImpl.getClass().getMethod(\"getSurfaceControl\").invoke(viewRootImpl);\n\n                    @SuppressLint(\"BlockedPrivateApi\") Method setBackgroundBlurRadius = SurfaceControl.Transaction.class.getDeclaredMethod(\"setBackgroundBlurRadius\", SurfaceControl.class, int.class);\n                    animator.addUpdateListener(animation -> {\n                        try {\n                            SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();\n                            var animatedValue = animation.getAnimatedValue();\n                            if (animatedValue != null) {\n                                setBackgroundBlurRadius.invoke(transaction, surfaceControl, (int) animatedValue);\n                            }\n                            transaction.apply();\n                        } catch (Throwable t) {\n                            Log.e(App.TAG, \"Blur behind dialog builder\", t);\n                        }\n                    });\n                } catch (Throwable t) {\n                    Log.e(App.TAG, \"Blur behind dialog builder\", t);\n                }\n                view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n                    @Override\n                    public void onViewAttachedToWindow(@NonNull View v) {\n                    }\n\n                    @Override\n                    public void onViewDetachedFromWindow(@NonNull View v) {\n                        animator.cancel();\n                    }\n                });\n                animator.start();\n            }\n        }\n    }\n\n    public static boolean getSystemProperty(String key, boolean defaultValue) {\n        boolean value = defaultValue;\n        try {\n            Class<?> c = Class.forName(\"android.os.SystemProperties\");\n            Method get = c.getMethod(\"getBoolean\", String.class, boolean.class);\n            value = (boolean) get.invoke(c, key, defaultValue);\n        } catch (Exception e) {\n            Log.e(App.TAG, \"Blur behind dialog builder get system property\", e);\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/dialog/FlashDialogBuilder.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.dialog;\n\nimport static org.lsposed.manager.App.TAG;\n\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.graphics.Typeface;\nimport android.os.ParcelFileDescriptor;\nimport android.text.method.LinkMovementMethod;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AlertDialog;\n\nimport com.google.android.material.textview.MaterialTextView;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.DialogTitleBinding;\nimport org.lsposed.manager.databinding.ScrollableDialogBinding;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\n\nimport rikka.widget.borderview.BorderNestedScrollView;\n\npublic class FlashDialogBuilder extends BlurBehindDialogBuilder {\n    private final String zipPath;\n    private final TextView textView;\n    private final BorderNestedScrollView rootView;\n\n    public FlashDialogBuilder(@NonNull Context context, DialogInterface.OnClickListener cancel) {\n        super(context, R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons);\n        var pref = App.getPreferences();\n        var notes = pref.getString(\"release_notes\", \"\");\n        this.zipPath = pref.getString(\"zip_file\", null);\n        LayoutInflater inflater = LayoutInflater.from(context);\n\n        var title = DialogTitleBinding.inflate(inflater).getRoot();\n        title.setText(R.string.update_lsposed);\n        setCustomTitle(title);\n\n        textView = new MaterialTextView(context);\n        var text = notes + \"\\n\\n\\n\" + context.getString(R.string.update_lsposed_msg) + \"\\n\\n\";\n        textView.setText(text);\n        textView.setMovementMethod(LinkMovementMethod.getInstance());\n        textView.setTextIsSelectable(true);\n\n        var binding = ScrollableDialogBinding.inflate(inflater, null, false);\n        binding.dialogContainer.addView(textView);\n        rootView = binding.getRoot();\n        setView(rootView);\n        title.setOnClickListener(v -> rootView.smoothScrollTo(0, 0));\n\n        setNegativeButton(android.R.string.cancel, cancel);\n        setPositiveButton(R.string.install, null);\n        setCancelable(false);\n    }\n\n    @Override\n    public AlertDialog show() {\n        var dialog = super.show();\n        var button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);\n        rootView.setBorderVisibilityChangedListener((t, ot, b, ob) -> button.setEnabled(!b));\n        button.setOnClickListener((v) -> {\n            rootView.setBorderVisibilityChangedListener(null);\n            setFlashView(v, dialog);\n        });\n        return dialog;\n    }\n\n    private void setFlashView(View view, AlertDialog dialog) {\n        var positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);\n        var negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);\n        positiveButton.setEnabled(false);\n        positiveButton.setText(android.R.string.ok);\n        positiveButton.setOnClickListener((v) -> dialog.dismiss());\n        negativeButton.setVisibility(View.GONE);\n\n        textView.setText(\"\");\n        textView.setTypeface(Typeface.MONOSPACE);\n        App.getExecutorService().submit(() -> flash(view, positiveButton));\n    }\n\n    private void flash(View view, Button button) {\n        try {\n            var pipe = ParcelFileDescriptor.createReliablePipe();\n            var readSide = pipe[0];\n            var writeSide = pipe[1];\n\n            ConfigManager.flashZip(zipPath, writeSide);\n            writeSide.close();\n\n            var inputStream = new ParcelFileDescriptor.AutoCloseInputStream(readSide);\n            var reader = new BufferedReader(new InputStreamReader(inputStream));\n\n            for (var line = \"\"; line != null; line = reader.readLine()) {\n                if (line.length() > 0) {\n                    var showLine = line + \"\\n\";\n                    view.post(() -> {\n                        textView.append(showLine);\n                        rootView.fullScroll(View.FOCUS_DOWN);\n                    });\n                }\n            }\n\n            reader.close();\n        } catch (IOException e) {\n            Log.e(TAG, \"flash\", e);\n            view.post(() -> textView.append(\"\\n\\n\" + e.getMessage()));\n            rootView.fullScroll(View.FOCUS_DOWN);\n        }\n\n        view.post(() -> button.setEnabled(true));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/dialog/WelcomeDialog.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.dialog;\n\nimport android.app.Dialog;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.fragment.app.FragmentManager;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.ui.fragment.BaseFragment;\nimport org.lsposed.manager.util.ShortcutUtil;\n\npublic class WelcomeDialog extends DialogFragment {\n    private static boolean shown = false;\n\n    private Dialog parasiticDialog(BlurBehindDialogBuilder builder) {\n        var shortcutSupported = ShortcutUtil.isRequestPinShortcutSupported(requireContext());\n        builder\n                .setTitle(R.string.parasitic_welcome)\n                .setMessage(shortcutSupported ? R.string.parasitic_welcome_summary :\n                        R.string.parasitic_welcome_summary_no_shortcut_support)\n                .setNegativeButton(R.string.never_show, (dialog, which) ->\n                        App.getPreferences().edit().putBoolean(\"never_show_welcome\", true).apply())\n                .setPositiveButton(android.R.string.ok, null)\n                .setNeutralButton(R.string.create_shortcut, (dialog, which) -> {\n                    var home = (BaseFragment) getParentFragment();\n                    if (!ShortcutUtil.requestPinLaunchShortcut(() -> {\n                        App.getPreferences().edit().putBoolean(\"never_show_welcome\", true).apply();\n                        if (home != null) {\n                            home.showHint(R.string.settings_shortcut_pinned_hint, false);\n                        }\n                    })) {\n                        if (home != null) {\n                            home.showHint(R.string.settings_unsupported_pin_shortcut_summary, false);\n                        }\n                    }\n                });\n        return builder.create();\n    }\n\n    private Dialog appDialog(BlurBehindDialogBuilder builder) {\n\n        return builder\n                .setTitle(R.string.app_welcome)\n                .setMessage(R.string.app_welcome_summary)\n                .setNegativeButton(R.string.never_show, (d, w) ->\n                        App.getPreferences().edit().putBoolean(\"never_show_welcome\", true).apply())\n                .setPositiveButton(android.R.string.ok, null)\n                .create();\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {\n        var builder = new BlurBehindDialogBuilder(requireContext(),\n                R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons);\n        if (App.isParasitic) {\n            return parasiticDialog(builder);\n        } else {\n            return appDialog(builder);\n        }\n    }\n\n    public static void showIfNeed(FragmentManager fm) {\n        if (shown) return;\n        if (!ConfigManager.isBinderAlive() ||\n                App.getPreferences().getBoolean(\"never_show_welcome\", false) ||\n                (App.isParasitic && ShortcutUtil.isLaunchShortcutPinned())) {\n            shown = true;\n            return;\n        }\n        new WelcomeDialog().show(fm, \"welcome\");\n        shown = true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/AppListFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.core.view.MenuProvider;\nimport androidx.recyclerview.widget.ConcatAdapter;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.adapters.AppHelper;\nimport org.lsposed.manager.adapters.ScopeAdapter;\nimport org.lsposed.manager.databinding.FragmentAppListBinding;\nimport org.lsposed.manager.util.BackupUtils;\nimport org.lsposed.manager.util.ModuleUtil;\n\nimport rikka.material.app.LocaleDelegate;\nimport rikka.recyclerview.RecyclerViewKt;\n\npublic class AppListFragment extends BaseFragment implements MenuProvider {\n\n    public SearchView searchView;\n    private ScopeAdapter scopeAdapter;\n    private ModuleUtil.InstalledModule module;\n\n    private SearchView.OnQueryTextListener searchListener;\n    public FragmentAppListBinding binding;\n    public ActivityResultLauncher<String> backupLauncher;\n    public ActivityResultLauncher<String[]> restoreLauncher;\n\n    private final RecyclerView.AdapterDataObserver observer = new RecyclerView.AdapterDataObserver() {\n        @Override\n        public void onChanged() {\n            if (binding != null && scopeAdapter != null) {\n                binding.swipeRefreshLayout.setRefreshing(!scopeAdapter.isLoaded());\n            }\n        }\n    };\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        binding = FragmentAppListBinding.inflate(getLayoutInflater(), container, false);\n        if (module == null) {\n            return binding.getRoot();\n        }\n        binding.appBar.setLiftable(true);\n        String title;\n        if (module.userId != 0) {\n            title = String.format(LocaleDelegate.getDefaultLocale(), \"%s (%d)\", module.getAppName(), module.userId);\n        } else {\n            title = module.getAppName();\n        }\n        binding.toolbar.setSubtitle(module.packageName);\n\n        scopeAdapter = new ScopeAdapter(this, module);\n        scopeAdapter.setHasStableIds(true);\n        scopeAdapter.registerAdapterDataObserver(observer);\n        var concatAdapter = new ConcatAdapter();\n        concatAdapter.addAdapter(scopeAdapter.switchAdaptor);\n        concatAdapter.addAdapter(scopeAdapter);\n        binding.recyclerView.setAdapter(concatAdapter);\n        binding.recyclerView.setHasFixedSize(true);\n        binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));\n        binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setLifted(!top));\n        RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);\n        binding.swipeRefreshLayout.setOnRefreshListener(() -> scopeAdapter.refresh(true));\n        binding.swipeRefreshLayout.setProgressViewEndTarget(true, binding.swipeRefreshLayout.getProgressViewEndOffset());\n        Intent intent = AppHelper.getSettingsIntent(module.packageName, module.userId);\n        if (intent == null) {\n            binding.fab.setVisibility(View.GONE);\n        } else {\n            binding.fab.setVisibility(View.VISIBLE);\n            binding.fab.setOnClickListener(v -> ConfigManager.startActivityAsUserWithFeature(intent, module.userId));\n        }\n        searchListener = scopeAdapter.getSearchListener();\n\n        setupToolbar(binding.toolbar, binding.clickView, title, R.menu.menu_app_list, view -> requireActivity().getOnBackPressedDispatcher().onBackPressed());\n        View.OnClickListener l = v -> {\n            if (searchView.isIconified()) {\n                binding.recyclerView.smoothScrollToPosition(0);\n                binding.appBar.setExpanded(true, true);\n            }\n        };\n        binding.toolbar.setOnClickListener(l);\n        binding.clickView.setOnClickListener(l);\n\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        if (module == null) {\n            if (!safeNavigate(R.id.action_app_list_fragment_to_modules_fragment)) {\n                safeNavigate(R.id.modules_nav);\n            }\n        }\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        AppListFragmentArgs args = AppListFragmentArgs.fromBundle(getArguments());\n        String modulePackageName = args.getModulePackageName();\n        int moduleUserId = args.getModuleUserId();\n\n        module = ModuleUtil.getInstance().getModule(modulePackageName, moduleUserId);\n        if (module == null) {\n            if (!safeNavigate(R.id.action_app_list_fragment_to_modules_fragment)) {\n                safeNavigate(R.id.modules_nav);\n            }\n        }\n\n        backupLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(\"application/gzip\"),\n                uri -> {\n                    if (uri == null) return;\n                    runAsync(() -> {\n                        try {\n                            BackupUtils.backup(uri, modulePackageName);\n                        } catch (Exception e) {\n                            var text = App.getInstance().getString(R.string.settings_backup_failed2, e.getMessage());\n                            showHint(text, false);\n                        }\n                    });\n                });\n        restoreLauncher = registerForActivityResult(new ActivityResultContracts.OpenDocument(),\n                uri -> {\n                    if (uri == null) return;\n                    runAsync(() -> {\n                        try {\n                            BackupUtils.restore(uri, modulePackageName);\n                        } catch (Exception e) {\n                            var text = App.getInstance().getString(R.string.settings_restore_failed2, e.getMessage());\n                            showHint(text, false);\n                        }\n                    });\n                });\n\n        requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {\n            @Override\n            public void handleOnBackPressed() {\n                scopeAdapter.onBackPressed();\n            }\n        });\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        if (scopeAdapter != null) scopeAdapter.refresh();\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        if (scopeAdapter != null) scopeAdapter.unregisterAdapterDataObserver(observer);\n        binding = null;\n    }\n\n    @Override\n    public boolean onMenuItemSelected(@NonNull MenuItem item) {\n        return scopeAdapter.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onPrepareMenu(@NonNull Menu menu) {\n        searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();\n        searchView.setOnQueryTextListener(searchListener);\n        searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n            @Override\n            public void onViewAttachedToWindow(View arg0) {\n                binding.appBar.setExpanded(false, true);\n                binding.recyclerView.setNestedScrollingEnabled(false);\n            }\n\n            @Override\n            public void onViewDetachedFromWindow(View v) {\n                binding.recyclerView.setNestedScrollingEnabled(true);\n            }\n        });\n        searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);\n        scopeAdapter.onPrepareOptionsMenu(menu);\n    }\n\n    @Override\n    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n\n    }\n\n    @Override\n    public boolean onContextItemSelected(@NonNull MenuItem item) {\n        if (scopeAdapter.onContextItemSelected(item)) {\n            return true;\n        }\n        return super.onContextItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/BaseFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.view.View;\nimport android.widget.Toast;\n\nimport androidx.annotation.IdRes;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.widget.Toolbar;\nimport androidx.core.view.MenuProvider;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.NavOptions;\nimport androidx.navigation.fragment.NavHostFragment;\n\nimport com.google.android.material.floatingactionbutton.FloatingActionButton;\nimport com.google.android.material.snackbar.Snackbar;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.util.AccessibilityUtils;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.FutureTask;\n\npublic abstract class BaseFragment extends Fragment {\n\n    public void navigateUp() {\n        getNavController().navigateUp();\n    }\n\n    public NavController getNavController() {\n        return NavHostFragment.findNavController(this);\n    }\n\n    public boolean safeNavigate(@IdRes int resId) {\n        try {\n            if (!AccessibilityUtils.isAnimationEnabled(requireContext().getContentResolver())) {\n                var clearedNavOptions = new NavOptions.Builder().build();\n                getNavController().navigate(resId, clearedNavOptions);\n            } else {\n                getNavController().navigate(resId);\n            }\n            return true;\n        } catch (IllegalArgumentException ignored) {\n            return false;\n        }\n    }\n\n    public boolean safeNavigate(NavDirections direction) {\n        try {\n            if (!AccessibilityUtils.isAnimationEnabled(requireContext().getContentResolver())) {\n                var clearedNavOptions = new NavOptions.Builder().build();\n                getNavController().navigate(direction, clearedNavOptions);\n            } else {\n                getNavController().navigate(direction);\n            }\n            return true;\n        } catch (IllegalArgumentException ignored) {\n            return false;\n        }\n    }\n\n    public void setupToolbar(Toolbar toolbar, View tipsView, int title) {\n        setupToolbar(toolbar, tipsView, getString(title), -1);\n    }\n\n    public void setupToolbar(Toolbar toolbar, View tipsView, int title, int menu) {\n        setupToolbar(toolbar, tipsView, getString(title), menu, null);\n    }\n\n    public void setupToolbar(Toolbar toolbar, View tipsView, String title, int menu) {\n        setupToolbar(toolbar, tipsView, title, menu, null);\n    }\n\n    public void setupToolbar(Toolbar toolbar, View tipsView, String title, int menu, View.OnClickListener navigationOnClickListener) {\n        toolbar.setNavigationOnClickListener(navigationOnClickListener == null ? (v -> navigateUp()) : navigationOnClickListener);\n        toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24);\n        toolbar.setTitle(title);\n        toolbar.setTooltipText(title);\n        if (tipsView != null) tipsView.setTooltipText(title);\n        if (menu != -1) {\n            toolbar.inflateMenu(menu);\n            if (this instanceof MenuProvider self) {\n                toolbar.setOnMenuItemClickListener(self::onMenuItemSelected);\n                self.onPrepareMenu(toolbar.getMenu());\n            }\n        }\n    }\n\n    public void runAsync(Runnable runnable) {\n        App.getExecutorService().submit(runnable);\n    }\n\n    public <T> Future<T> runAsync(Callable<T> callable) {\n        return App.getExecutorService().submit(callable);\n    }\n\n    public void runOnUiThread(Runnable runnable) {\n        App.getMainHandler().post(runnable);\n    }\n\n    public <T> Future<T> runOnUiThread(Callable<T> callable) {\n        var task = new FutureTask<>(callable);\n        runOnUiThread(task);\n        return task;\n    }\n\n    public void showHint(@StringRes int res, boolean lengthShort, @StringRes int actionRes, View.OnClickListener action) {\n        showHint(App.getInstance().getString(res), lengthShort, App.getInstance().getString(actionRes), action);\n    }\n\n    public void showHint(@StringRes int res, boolean lengthShort) {\n        showHint(App.getInstance().getString(res), lengthShort, null, null);\n    }\n\n    public void showHint(CharSequence str, boolean lengthShort) {\n        showHint(str, lengthShort, null, null);\n    }\n\n    public void showHint(CharSequence str, boolean lengthShort, CharSequence actionStr, View.OnClickListener action) {\n        var container = getView();\n        if (isResumed() && container != null) {\n            var snackbar = Snackbar.make(container, str, lengthShort ? Snackbar.LENGTH_SHORT : Snackbar.LENGTH_LONG);\n            var fab = container.findViewById(R.id.fab);\n            if (fab instanceof FloatingActionButton && ((FloatingActionButton) fab).isOrWillBeShown())\n                snackbar.setAnchorView(fab);\n            if (actionStr != null && action != null) snackbar.setAction(actionStr, action);\n            snackbar.show();\n            return;\n        }\n        runOnUiThread(() -> {\n            try {\n                Toast.makeText(App.getInstance(), str, lengthShort ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();\n            } catch (Throwable ignored) {\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/CompileDialogFragment.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatDialogFragment;\nimport androidx.fragment.app.FragmentManager;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.FragmentCompileDialogBinding;\nimport org.lsposed.manager.receivers.LSPManagerServiceHolder;\nimport org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;\n\nimport java.lang.ref.WeakReference;\n\n@SuppressWarnings(\"deprecation\")\npublic class CompileDialogFragment extends AppCompatDialogFragment {\n    public static void speed(FragmentManager fragmentManager, ApplicationInfo info) {\n        CompileDialogFragment fragment = new CompileDialogFragment();\n        fragment.setCancelable(false);\n        var bundle = new Bundle();\n        bundle.putParcelable(\"appInfo\", info);\n        fragment.setArguments(bundle);\n        fragment.show(fragmentManager, \"compile_dialog\");\n    }\n\n    @Override\n    @NonNull\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        var arguments = getArguments();\n        ApplicationInfo appInfo = arguments != null ? arguments.getParcelable(\"appInfo\") : null;\n        if (appInfo == null) {\n            throw new IllegalStateException(\"appInfo should not be null.\");\n        }\n\n        FragmentCompileDialogBinding binding = FragmentCompileDialogBinding.inflate(LayoutInflater.from(requireActivity()), null, false);\n        final PackageManager pm = requireContext().getPackageManager();\n        var builder = new BlurBehindDialogBuilder(requireActivity())\n                .setIcon(appInfo.loadIcon(pm))\n                .setTitle(appInfo.loadLabel(pm))\n                .setView(binding.getRoot());\n\n        var alertDialog = builder.create();\n        new CompileTask(this).executeOnExecutor(App.getExecutorService(), appInfo.packageName);\n        return alertDialog;\n    }\n\n    private static class CompileTask extends AsyncTask<String, Void, Throwable> {\n\n        WeakReference<CompileDialogFragment> outerRef;\n\n        CompileTask(CompileDialogFragment fragment) {\n            outerRef = new WeakReference<>(fragment);\n        }\n\n        @Override\n        protected Throwable doInBackground(String... commands) {\n            try {\n                LSPManagerServiceHolder.getService().clearApplicationProfileData(commands[0]);\n                if (LSPManagerServiceHolder.getService().performDexOptMode(commands[0])) {\n                    return null;\n                } else {\n                    return new UnknownError();\n                }\n            } catch (Throwable e) {\n                return e;\n            }\n        }\n\n        @Override\n        protected void onPostExecute(Throwable result) {\n            Context context = App.getInstance();\n            String text;\n            if (result != null) {\n                if (result instanceof UnknownError) {\n                    text = context.getString(R.string.compile_failed);\n                } else {\n                    text = context.getString(R.string.compile_failed_with_info) + result;\n                }\n            } else {\n                text = context.getString(R.string.compile_done);\n            }\n            try {\n                CompileDialogFragment fragment = outerRef.get();\n                if (fragment != null) {\n                    fragment.dismissAllowingStateLoss();\n                    var parent = fragment.getParentFragment();\n                    if (parent instanceof BaseFragment) {\n                        ((BaseFragment) parent).showHint(text, true);\n                    }\n                }\n            } catch (IllegalStateException ignored) {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.system.OsConstants;\nimport android.text.method.LinkMovementMethod;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.text.HtmlCompat;\nimport androidx.core.view.MenuProvider;\nimport androidx.fragment.app.DialogFragment;\n\nimport org.lsposed.lspd.ILSPManagerService;\nimport org.lsposed.manager.BuildConfig;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.DialogAboutBinding;\nimport org.lsposed.manager.databinding.FragmentHomeBinding;\nimport org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;\nimport org.lsposed.manager.ui.dialog.FlashDialogBuilder;\nimport org.lsposed.manager.ui.dialog.WelcomeDialog;\nimport org.lsposed.manager.util.NavUtil;\nimport org.lsposed.manager.util.UpdateUtil;\nimport org.lsposed.manager.util.chrome.LinkTransformationMethod;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport rikka.core.util.ClipboardUtils;\nimport rikka.material.app.LocaleDelegate;\n\npublic class HomeFragment extends BaseFragment implements MenuProvider {\n    private FragmentHomeBinding binding;\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        WelcomeDialog.showIfNeed(getChildFragmentManager());\n    }\n\n    @Override\n    public void onPrepareMenu(Menu menu) {\n        menu.findItem(R.id.menu_about).setOnMenuItemClickListener(v -> {\n            showAbout();\n            return true;\n        });\n        menu.findItem(R.id.menu_issue).setOnMenuItemClickListener(v -> {\n            NavUtil.startURL(requireActivity(), \"https://github.com/JingMatrix/LSPosed/issues/new/choose\");\n            return true;\n        });\n    }\n\n    @Override\n    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n\n    }\n\n    @Override\n    public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {\n        return false;\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        binding = FragmentHomeBinding.inflate(inflater, container, false);\n        setupToolbar(binding.toolbar, binding.clickView, R.string.app_name, R.menu.menu_home);\n        binding.toolbar.setNavigationIcon(null);\n        binding.toolbar.setOnClickListener(v -> showAbout());\n        binding.clickView.setOnClickListener(v -> showAbout());\n        binding.appBar.setLiftable(true);\n        binding.nestedScrollView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setLifted(!top));\n\n        updateStates(requireActivity(), ConfigManager.isBinderAlive(), UpdateUtil.needUpdate());\n\n        return binding.getRoot();\n    }\n\n    private void updateStates(Activity activity, boolean binderAlive, boolean needUpdate) {\n        if (binderAlive) {\n            if (needUpdate) {\n                binding.updateTitle.setText(R.string.need_update);\n                binding.updateSummary.setText(getString(R.string.please_update_summary));\n                binding.statusIcon.setImageResource(R.drawable.ic_round_update_24);\n                binding.updateBtn.setOnClickListener(v -> {\n                    if (UpdateUtil.canInstall()) {\n                        new FlashDialogBuilder(activity, null).show();\n                    } else {\n                        NavUtil.startURL(activity, getString(R.string.latest_url));\n                    }\n                });\n                binding.updateCard.setVisibility(View.VISIBLE);\n            } else {\n                binding.updateCard.setVisibility(View.GONE);\n            }\n            boolean dex2oatAbnormal = ConfigManager.getDex2OatWrapperCompatibility() != ILSPManagerService.DEX2OAT_OK && !ConfigManager.dex2oatFlagsLoaded();\n            var sepolicyAbnormal = !ConfigManager.isSepolicyLoaded();\n            var systemServerAbnormal = !ConfigManager.systemServerRequested();\n            if (sepolicyAbnormal || systemServerAbnormal || dex2oatAbnormal) {\n                binding.statusTitle.setText(R.string.partial_activated);\n                binding.statusIcon.setImageResource(R.drawable.ic_round_warning_24);\n                binding.warningCard.setVisibility(View.VISIBLE);\n                if (sepolicyAbnormal) {\n                    binding.warningTitle.setText(R.string.selinux_policy_not_loaded_summary);\n                    binding.warningSummary.setText(HtmlCompat.fromHtml(getString(R.string.selinux_policy_not_loaded), HtmlCompat.FROM_HTML_MODE_LEGACY));\n                }\n                if (systemServerAbnormal) {\n                    binding.warningTitle.setText(R.string.system_inject_fail_summary);\n                    binding.warningSummary.setText(HtmlCompat.fromHtml(getString(R.string.system_inject_fail), HtmlCompat.FROM_HTML_MODE_LEGACY));\n                }\n                if (dex2oatAbnormal) {\n                    binding.warningTitle.setText(R.string.system_prop_incorrect_summary);\n                    binding.warningSummary.setText(HtmlCompat.fromHtml(getString(R.string.system_prop_incorrect), HtmlCompat.FROM_HTML_MODE_LEGACY));\n                }\n            } else {\n                binding.warningCard.setVisibility(View.GONE);\n                binding.statusTitle.setText(R.string.activated);\n                binding.statusIcon.setImageResource(R.drawable.ic_round_check_circle_24);\n            }\n            binding.statusSummary.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%d) - %s\",\n                    ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode(), ConfigManager.getApi()));\n            binding.developerWarningCard.setVisibility(isDeveloper() ? View.VISIBLE : View.GONE);\n        } else {\n            boolean isMagiskInstalled = ConfigManager.isMagiskInstalled();\n            if (isMagiskInstalled) {\n                binding.updateTitle.setText(R.string.install);\n                binding.updateSummary.setText(R.string.install_summary);\n                binding.statusIcon.setImageResource(R.drawable.ic_round_error_outline_24);\n                binding.updateBtn.setOnClickListener(v -> {\n                    if (UpdateUtil.canInstall()) {\n                        new FlashDialogBuilder(activity, null).show();\n                    } else {\n                        NavUtil.startURL(activity, getString(R.string.install_url));\n                    }\n                });\n                binding.updateCard.setVisibility(View.VISIBLE);\n            } else {\n                binding.updateCard.setVisibility(View.GONE);\n            }\n            binding.warningCard.setVisibility(View.GONE);\n            binding.statusTitle.setText(R.string.not_installed);\n            binding.statusSummary.setText(R.string.not_install_summary);\n        }\n\n        if (ConfigManager.isBinderAlive()) {\n            binding.apiVersion.setText(String.valueOf(ConfigManager.getXposedApiVersion()));\n            binding.api.setText(ConfigManager.isDexObfuscateEnabled() ? R.string.enabled : R.string.not_enabled);\n            binding.frameworkVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%1$s (%2$d)\", ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode()));\n            binding.managerPackageName.setText(activity.getPackageName());\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {\n                binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%s)\", getString(R.string.unsupported), getString(R.string.android_version_unsatisfied)));\n            } else switch (ConfigManager.getDex2OatWrapperCompatibility()) {\n                case ILSPManagerService.DEX2OAT_OK ->\n                        binding.dex2oatWrapper.setText(R.string.supported);\n                case ILSPManagerService.DEX2OAT_CRASHED ->\n                        binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%s)\", getString(R.string.unsupported), getString(R.string.crashed)));\n                case ILSPManagerService.DEX2OAT_MOUNT_FAILED ->\n                        binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%s)\", getString(R.string.unsupported), getString(R.string.mount_failed)));\n                case ILSPManagerService.DEX2OAT_SELINUX_PERMISSIVE ->\n                        binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%s)\", getString(R.string.unsupported), getString(R.string.selinux_permissive)));\n                case ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT ->\n                        binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%s)\", getString(R.string.unsupported), getString(R.string.sepolicy_incorrect)));\n            }\n        } else {\n            binding.apiVersion.setText(R.string.not_installed);\n            binding.api.setText(R.string.not_installed);\n            binding.frameworkVersion.setText(R.string.not_installed);\n            binding.managerPackageName.setText(activity.getPackageName());\n        }\n\n        if (Build.VERSION.PREVIEW_SDK_INT != 0) {\n            binding.systemVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%1$s Preview (API %2$d)\", Build.VERSION.CODENAME, Build.VERSION.SDK_INT));\n        } else {\n            binding.systemVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%1$s (API %2$d)\", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));\n        }\n\n        binding.device.setText(getDevice());\n        binding.systemAbi.setText(Build.SUPPORTED_ABIS[0]);\n        String info = activity.getString(R.string.info_api_version) +\n                \"\\n\" +\n                binding.apiVersion.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.settings_xposed_api_call_protection) +\n                \"\\n\" +\n                binding.api.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.info_dex2oat_wrapper) +\n                \"\\n\" +\n                binding.dex2oatWrapper.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.info_framework_version) +\n                \"\\n\" +\n                binding.frameworkVersion.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.info_manager_package_name) +\n                \"\\n\" +\n                binding.managerPackageName.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.info_system_version) +\n                \"\\n\" +\n                binding.systemVersion.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.info_device) +\n                \"\\n\" +\n                binding.device.getText() +\n                \"\\n\\n\" +\n                activity.getString(R.string.info_system_abi) +\n                \"\\n\" +\n                binding.systemAbi.getText();\n        var map = new HashMap<String, String>();\n        map.put(\"apiVersion\", binding.apiVersion.getText().toString());\n        map.put(\"api\", binding.api.getText().toString());\n        map.put(\"frameworkVersion\", binding.frameworkVersion.getText().toString());\n        map.put(\"systemAbi\", Arrays.toString(Build.SUPPORTED_ABIS));\n        binding.copyInfo.setOnClickListener(v -> {\n            ClipboardUtils.put(activity, info);\n            showHint(R.string.info_copied, false);\n        });\n    }\n\n    private String getDevice() {\n        String manufacturer = Character.toUpperCase(Build.MANUFACTURER.charAt(0)) + Build.MANUFACTURER.substring(1);\n        if (!Build.BRAND.equals(Build.MANUFACTURER)) {\n            manufacturer += \" \" + Character.toUpperCase(Build.BRAND.charAt(0)) + Build.BRAND.substring(1);\n        }\n        manufacturer += \" \" + Build.MODEL + \" \";\n        return manufacturer;\n    }\n\n    private boolean isDeveloper() {\n        var developer = new AtomicBoolean(false);\n        var pids = Paths.get(\"/data/local/tmp/.studio/ipids\");\n        try (var dir = Files.list(pids)) {\n            dir.findFirst().ifPresent(name -> {\n                var pid = Integer.parseInt(name.getFileName().toString());\n                try {\n                    Os.kill(pid, 0);\n                    developer.set(true);\n                } catch (ErrnoException e) {\n                    if (e.errno == OsConstants.ESRCH) {\n                        try {\n                            Files.delete(name);\n                        } catch (IOException ignored) {\n                        }\n                    } else {\n                        developer.set(true);\n                    }\n                }\n            });\n        } catch (IOException e) {\n            return false;\n        }\n        return developer.get();\n    }\n\n    public static class AboutDialog extends DialogFragment {\n        @NonNull\n        @Override\n        public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {\n            DialogAboutBinding binding = DialogAboutBinding.inflate(getLayoutInflater(), null, false);\n            binding.designAboutTitle.setText(R.string.app_name);\n            binding.designAboutInfo.setMovementMethod(LinkMovementMethod.getInstance());\n            binding.designAboutInfo.setTransformationMethod(new LinkTransformationMethod(requireActivity()));\n            binding.designAboutInfo.setText(HtmlCompat.fromHtml(getString(\n                    R.string.about_view_source_code,\n                    \"<b><a href=\\\"https://github.com/JingMatrix/LSPosed\\\">GitHub</a></b>\",\n                    \"<b><a href=\\\"https://t.me/LSPosed\\\">Telegram</a></b>\"), HtmlCompat.FROM_HTML_MODE_LEGACY));\n            binding.designAboutVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%d)\", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));\n            return new BlurBehindDialogBuilder(requireContext())\n                    .setView(binding.getRoot()).create();\n        }\n    }\n\n    private void showAbout() {\n        new AboutDialog().show(getChildFragmentManager(), \"about\");\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        binding = null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.annotation.SuppressLint;\nimport android.content.ActivityNotFoundException;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.HorizontalScrollView;\n\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.MenuProvider;\nimport androidx.fragment.app.Fragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.viewpager2.adapter.FragmentStateAdapter;\n\nimport com.google.android.material.tabs.TabLayout;\nimport com.google.android.material.tabs.TabLayoutMediator;\nimport com.google.android.material.textview.MaterialTextView;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.FragmentPagerBinding;\nimport org.lsposed.manager.databinding.ItemLogTextviewBinding;\nimport org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;\nimport org.lsposed.manager.receivers.LSPManagerServiceHolder;\nimport org.lsposed.manager.ui.widget.EmptyStateRecyclerView;\nimport org.lsposed.manager.util.AccessibilityUtils;\n\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.InputStreamReader;\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport rikka.material.app.LocaleDelegate;\nimport rikka.recyclerview.RecyclerViewKt;\n\npublic class LogsFragment extends BaseFragment implements MenuProvider {\n    private FragmentPagerBinding binding;\n    private LogPageAdapter adapter;\n    private MenuItem wordWrap;\n\n    interface OptionsItemSelectListener {\n        boolean onOptionsItemSelected(@NonNull MenuItem item);\n    }\n\n    private OptionsItemSelectListener optionsItemSelectListener;\n\n    private final ActivityResultLauncher<String> saveLogsLauncher = registerForActivityResult(\n            new ActivityResultContracts.CreateDocument(\"application/zip\"),\n            uri -> {\n                if (uri == null) return;\n                runAsync(() -> {\n                    var context = requireContext();\n                    var cr = context.getContentResolver();\n                    try (var zipFd = cr.openFileDescriptor(uri, \"wt\")) {\n                        showHint(context.getString(R.string.logs_saving), false);\n                        LSPManagerServiceHolder.getService().getLogs(zipFd);\n                        showHint(context.getString(R.string.logs_saved), true);\n                    } catch (Throwable e) {\n                        var cause = e.getCause();\n                        var message = cause == null ? e.getMessage() : cause.getMessage();\n                        var text = context.getString(R.string.logs_save_failed2, message);\n                        showHint(text, false);\n                        Log.w(App.TAG, \"save log\", e);\n                    }\n                });\n            });\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        binding = FragmentPagerBinding.inflate(inflater, container, false);\n        binding.appBar.setLiftable(true);\n        setupToolbar(binding.toolbar, binding.clickView, R.string.Logs, R.menu.menu_logs);\n        binding.toolbar.setNavigationIcon(null);\n        binding.toolbar.setSubtitle(ConfigManager.isVerboseLogEnabled() ? R.string.enabled_verbose_log : R.string.disabled_verbose_log);\n        adapter = new LogPageAdapter(this);\n        binding.viewPager.setAdapter(adapter);\n\n        var isAnimationEnabled = AccessibilityUtils.isAnimationEnabled(requireContext().getContentResolver());\n        new TabLayoutMediator(\n            binding.tabLayout,\n            binding.viewPager,\n            // `autoRefresh = true` by default. Update the tabs automatically when the data set of the view pager's\n            // adapter changes.\n            true,\n            isAnimationEnabled,\n            (tab, position) -> tab.setText((int) adapter.getItemId(position))\n        ).attach();\n\n        binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {\n            ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);\n            int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();\n            if (tabLayoutWidth <= binding.getRoot().getWidth()) {\n                binding.tabLayout.setTabMode(TabLayout.MODE_FIXED);\n                binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);\n            }\n        });\n\n        return binding.getRoot();\n    }\n\n    public void setOptionsItemSelectListener(OptionsItemSelectListener optionsItemSelectListener) {\n        this.optionsItemSelectListener = optionsItemSelectListener;\n    }\n\n    @Override\n    public boolean onMenuItemSelected(@NonNull MenuItem item) {\n        var itemId = item.getItemId();\n        if (itemId == R.id.menu_save) {\n            save();\n            return true;\n        } else if (itemId == R.id.menu_word_wrap) {\n            item.setChecked(!item.isChecked());\n            App.getPreferences().edit().putBoolean(\"enable_word_wrap\", item.isChecked()).apply();\n            binding.viewPager.setUserInputEnabled(item.isChecked());\n            adapter.refresh();\n            return true;\n        }\n        if (optionsItemSelectListener != null) {\n            return optionsItemSelectListener.onOptionsItemSelected(item);\n        }\n        return false;\n    }\n\n    @Override\n    public void onPrepareMenu(@NonNull Menu menu) {\n        wordWrap = menu.findItem(R.id.menu_word_wrap);\n        wordWrap.setChecked(App.getPreferences().getBoolean(\"enable_word_wrap\", false));\n        binding.viewPager.setUserInputEnabled(wordWrap.isChecked());\n    }\n\n    @Override\n    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n\n        binding = null;\n    }\n\n    private void save() {\n        LocalDateTime now = LocalDateTime.now();\n        String filename = String.format(LocaleDelegate.getDefaultLocale(), \"LSPosed_%s.zip\", now.toString());\n        try {\n            saveLogsLauncher.launch(filename);\n        } catch (ActivityNotFoundException e) {\n            showHint(R.string.enable_documentui, true);\n        }\n    }\n\n    public static class LogFragment extends BaseFragment {\n        public static final int SCROLL_THRESHOLD = 500;\n        protected boolean verbose;\n        protected SwiperefreshRecyclerviewBinding binding;\n        protected LogAdaptor adaptor;\n        protected LinearLayoutManager layoutManager;\n\n        class LogAdaptor extends EmptyStateRecyclerView.EmptyStateAdapter<LogAdaptor.ViewHolder> {\n            private List<CharSequence> log = Collections.emptyList();\n            private boolean isLoaded = false;\n\n            @NonNull\n            @Override\n            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n                return new ViewHolder(ItemLogTextviewBinding.inflate(getLayoutInflater(), parent, false));\n            }\n\n            @Override\n            public void onBindViewHolder(@NonNull ViewHolder holder, int position) {\n                holder.item.setText(log.get(position));\n            }\n\n            @Override\n            public int getItemCount() {\n                return log.size();\n            }\n\n            @SuppressLint(\"NotifyDataSetChanged\")\n            void refresh(List<CharSequence> log) {\n                runOnUiThread(() -> {\n                    isLoaded = true;\n                    this.log = log;\n                    notifyDataSetChanged();\n                });\n            }\n\n            void fullRefresh() {\n                runAsync(() -> {\n                    isLoaded = false;\n                    List<CharSequence> tmp;\n                    try (var parcelFileDescriptor = ConfigManager.getLog(verbose);\n                         var br = new BufferedReader(new InputStreamReader(new FileInputStream(parcelFileDescriptor != null ? parcelFileDescriptor.getFileDescriptor() : null)))) {\n                        tmp = br.lines().parallel().collect(Collectors.toList());\n                    } catch (Throwable e) {\n                        tmp = Arrays.asList(Log.getStackTraceString(e).split(\"\\n\"));\n                    }\n                    refresh(tmp);\n                });\n            }\n\n            @Override\n            public boolean isLoaded() {\n                return isLoaded;\n            }\n\n            class ViewHolder extends RecyclerView.ViewHolder {\n                final MaterialTextView item;\n\n                public ViewHolder(ItemLogTextviewBinding binding) {\n                    super(binding.getRoot());\n                    item = binding.logItem;\n                }\n            }\n        }\n\n        protected LogAdaptor createAdaptor() {\n            return new LogAdaptor();\n        }\n\n        @Nullable\n        @Override\n        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n            binding = SwiperefreshRecyclerviewBinding.inflate(getLayoutInflater(), container, false);\n            var arguments = getArguments();\n            if (arguments == null) return null;\n            verbose = arguments.getBoolean(\"verbose\");\n            adaptor = createAdaptor();\n            binding.recyclerView.setAdapter(adaptor);\n            layoutManager = new LinearLayoutManager(requireActivity());\n            binding.recyclerView.setLayoutManager(layoutManager);\n            // ltr even for rtl languages because of log format\n            binding.recyclerView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);\n            binding.swipeRefreshLayout.setProgressViewEndTarget(true, binding.swipeRefreshLayout.getProgressViewEndOffset());\n            RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);\n            binding.swipeRefreshLayout.setOnRefreshListener(adaptor::fullRefresh);\n            adaptor.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n                @Override\n                public void onChanged() {\n                    binding.swipeRefreshLayout.setRefreshing(!adaptor.isLoaded());\n                }\n            });\n            adaptor.fullRefresh();\n            return binding.getRoot();\n        }\n\n        public void scrollToTop(LogsFragment logsFragment) {\n            logsFragment.binding.appBar.setExpanded(true, true);\n            if (layoutManager.findFirstVisibleItemPosition() > SCROLL_THRESHOLD) {\n                binding.recyclerView.scrollToPosition(0);\n            } else {\n                binding.recyclerView.smoothScrollToPosition(0);\n            }\n        }\n\n        public void scrollToBottom(LogsFragment logsFragment) {\n            logsFragment.binding.appBar.setExpanded(false, true);\n            var end = Math.max(adaptor.getItemCount() - 1, 0);\n            if (adaptor.getItemCount() - layoutManager.findLastVisibleItemPosition() > SCROLL_THRESHOLD) {\n                binding.recyclerView.scrollToPosition(end);\n            } else {\n                binding.recyclerView.smoothScrollToPosition(end);\n            }\n        }\n\n        void attachListeners() {\n            var parent = getParentFragment();\n            if (parent instanceof LogsFragment logsFragment) {\n                logsFragment.binding.appBar.setLifted(!binding.recyclerView.getBorderViewDelegate().isShowingTopBorder());\n                binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> logsFragment.binding.appBar.setLifted(!top));\n                logsFragment.setOptionsItemSelectListener(item -> {\n                    int itemId = item.getItemId();\n                    if (itemId == R.id.menu_scroll_top) {\n                        scrollToTop(logsFragment);\n                    } else if (itemId == R.id.menu_scroll_down) {\n                        scrollToBottom(logsFragment);\n                    } else if (itemId == R.id.menu_clear) {\n                        if (ConfigManager.clearLogs(verbose)) {\n                            logsFragment.showHint(R.string.logs_cleared, true);\n                            adaptor.fullRefresh();\n                        } else {\n                            logsFragment.showHint(R.string.logs_clear_failed_2, true);\n                        }\n                        return true;\n                    }\n                    return false;\n                });\n\n                View.OnClickListener l = v -> scrollToTop(logsFragment);\n                logsFragment.binding.clickView.setOnClickListener(l);\n                logsFragment.binding.toolbar.setOnClickListener(l);\n            }\n        }\n\n        void detachListeners() {\n            binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener(null);\n        }\n\n        @Override\n        public void onStart() {\n            super.onStart();\n            attachListeners();\n        }\n\n        @Override\n        public void onResume() {\n            super.onResume();\n            attachListeners();\n        }\n\n\n        @Override\n        public void onPause() {\n            super.onPause();\n            detachListeners();\n        }\n\n        @Override\n        public void onStop() {\n            super.onStop();\n            detachListeners();\n        }\n    }\n\n    public static class UnwrapLogFragment extends LogFragment {\n\n        @Nullable\n        @Override\n        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n            var root = super.onCreateView(inflater, container, savedInstanceState);\n            binding.swipeRefreshLayout.removeView(binding.recyclerView);\n            HorizontalScrollView horizontalScrollView = new HorizontalScrollView(getContext());\n            horizontalScrollView.setFillViewport(true);\n            horizontalScrollView.setHorizontalScrollBarEnabled(false);\n            horizontalScrollView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);\n            if (!AccessibilityUtils.isAnimationEnabled(requireContext().getContentResolver())) {\n                horizontalScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);\n            }\n            binding.swipeRefreshLayout.addView(horizontalScrollView);\n            horizontalScrollView.addView(binding.recyclerView);\n            binding.recyclerView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;\n            return root;\n        }\n\n        @Override\n        protected LogAdaptor createAdaptor() {\n            return new LogAdaptor() {\n                @Override\n                public void onBindViewHolder(@NonNull ViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    var view = holder.item;\n                    view.measure(0, 0);\n                    int desiredWidth = view.getMeasuredWidth();\n                    ViewGroup.LayoutParams layoutParams = view.getLayoutParams();\n                    layoutParams.width = desiredWidth;\n                    if (binding.recyclerView.getWidth() < desiredWidth) {\n                        binding.recyclerView.requestLayout();\n                    }\n                }\n            };\n        }\n    }\n\n    class LogPageAdapter extends FragmentStateAdapter {\n\n        public LogPageAdapter(@NonNull Fragment fragment) {\n            super(fragment);\n        }\n\n        @NonNull\n        @Override\n        public Fragment createFragment(int position) {\n            var bundle = new Bundle();\n            bundle.putBoolean(\"verbose\", verbose(position));\n            var f = getItemViewType(position) == 0 ? new LogFragment() : new UnwrapLogFragment();\n            f.setArguments(bundle);\n            return f;\n        }\n\n        @Override\n        public int getItemCount() {\n            return 2;\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return verbose(position) ? R.string.nav_item_logs_verbose : R.string.nav_item_logs_module;\n        }\n\n        @Override\n        public boolean containsItem(long itemId) {\n            return itemId == R.string.nav_item_logs_verbose || itemId == R.string.nav_item_logs_module;\n        }\n\n        public boolean verbose(int position) {\n            return position != 0;\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return wordWrap.isChecked() ? 0 : 1;\n        }\n\n        public void refresh() {\n            runOnUiThread(this::notifyDataSetChanged);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.text.style.ForegroundColorSpan;\nimport android.text.style.StyleSpan;\nimport android.text.style.TypefaceSpan;\nimport android.util.SparseArray;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Filter;\nimport android.widget.Filterable;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.MenuProvider;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavOptions;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.viewpager2.adapter.FragmentStateAdapter;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport com.bumptech.glide.request.target.CustomTarget;\nimport com.bumptech.glide.request.transition.Transition;\nimport com.google.android.material.behavior.HideBottomViewOnScrollBehavior;\nimport com.google.android.material.checkbox.MaterialCheckBox;\nimport com.google.android.material.floatingactionbutton.FloatingActionButton;\nimport com.google.android.material.tabs.TabLayout;\nimport com.google.android.material.tabs.TabLayoutMediator;\n\nimport org.lsposed.lspd.models.UserInfo;\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.adapters.AppHelper;\nimport org.lsposed.manager.databinding.FragmentPagerBinding;\nimport org.lsposed.manager.databinding.ItemModuleBinding;\nimport org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;\nimport org.lsposed.manager.ui.widget.EmptyStateRecyclerView;\nimport org.lsposed.manager.util.GlideApp;\nimport org.lsposed.manager.util.ModuleUtil;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.stream.IntStream;\n\nimport rikka.core.util.ResourceUtils;\nimport rikka.material.app.LocaleDelegate;\nimport rikka.recyclerview.RecyclerViewKt;\n\npublic class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleListener, RepoLoader.RepoListener, MenuProvider {\n    private static final PackageManager pm = App.getInstance().getPackageManager();\n    private static final ModuleUtil moduleUtil = ModuleUtil.getInstance();\n    private static final RepoLoader repoLoader = RepoLoader.getInstance();\n    protected FragmentPagerBinding binding;\n    protected SearchView searchView;\n    private SearchView.OnQueryTextListener searchListener;\n\n    SparseArray<ModuleAdapter> adapters = new SparseArray<>();\n    PagerAdapter pagerAdapter = null;\n\n    private ModuleUtil.InstalledModule selectedModule;\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        searchListener = new SearchView.OnQueryTextListener() {\n            @Override\n            public boolean onQueryTextSubmit(String query) {\n                forEachAdaptor(adapter -> adapter.getFilter().filter(query));\n                return false;\n            }\n\n            @Override\n            public boolean onQueryTextChange(String query) {\n                forEachAdaptor(adapter -> adapter.getFilter().filter(query));\n                return false;\n            }\n        };\n    }\n\n    private void forEachAdaptor(Consumer<? super ModuleAdapter> action) {\n        var snapshot = adapters;\n        for (var i = 0; i < snapshot.size(); ++i) {\n            action.accept(snapshot.valueAt(i));\n        }\n    }\n\n    private void showFab() {\n        var layoutParams = binding.fab.getLayoutParams();\n        if (layoutParams instanceof CoordinatorLayout.LayoutParams) {\n            var coordinatorLayoutBehavior =\n                    ((CoordinatorLayout.LayoutParams) layoutParams).getBehavior();\n            if (coordinatorLayoutBehavior instanceof HideBottomViewOnScrollBehavior) {\n                //noinspection unchecked\n                ((HideBottomViewOnScrollBehavior<FloatingActionButton>) coordinatorLayoutBehavior).slideUp(binding.fab);\n            }\n        }\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        binding = FragmentPagerBinding.inflate(inflater, container, false);\n        binding.appBar.setLiftable(true);\n        setupToolbar(binding.toolbar, binding.clickView, R.string.Modules, R.menu.menu_modules);\n        binding.toolbar.setNavigationIcon(null);\n        pagerAdapter = new PagerAdapter(this);\n        binding.viewPager.setAdapter(pagerAdapter);\n        binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {\n            @Override\n            public void onPageSelected(int position) {\n                showFab();\n            }\n        });\n\n        new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> {\n            if (position < adapters.size()) {\n                tab.setText(adapters.valueAt(position).getUser().name);\n            }\n        }).attach();\n\n        binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {\n            ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);\n            int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();\n            if (tabLayoutWidth <= binding.getRoot().getWidth()) {\n                binding.tabLayout.setTabMode(TabLayout.MODE_FIXED);\n                binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);\n            }\n        });\n\n        binding.fab.setOnClickListener(v -> {\n            var bundle = new Bundle();\n            var user = adapters.valueAt(binding.viewPager.getCurrentItem()).getUser();\n            bundle.putParcelable(\"userInfo\", user);\n            var f = new RecyclerViewDialogFragment();\n            f.setArguments(bundle);\n            f.show(getChildFragmentManager(), \"install_to_user\" + user.id);\n        });\n\n        moduleUtil.addListener(this);\n        repoLoader.addListener(this);\n        onModulesReloaded();\n\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onPrepareMenu(Menu menu) {\n        searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();\n        if (searchView != null) {\n            searchView.setOnQueryTextListener(searchListener);\n            searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n                @Override\n                public void onViewAttachedToWindow(@NonNull View arg0) {\n                    binding.appBar.setExpanded(false, true);\n                }\n\n                @Override\n                public void onViewDetachedFromWindow(@NonNull View v) {\n                }\n            });\n            searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);\n        }\n    }\n\n    @Override\n    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n\n    }\n\n    @Override\n    public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {\n        return false;\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        forEachAdaptor(ModuleAdapter::refresh);\n    }\n\n    @Override\n    public void onSingleModuleReloaded(ModuleUtil.InstalledModule module) {\n        forEachAdaptor(ModuleAdapter::refresh);\n    }\n\n    @Override\n    public void onModulesReloaded() {\n        var users = moduleUtil.getUsers();\n        if (users == null) return;\n\n        if (users.size() != 1) {\n            binding.viewPager.setUserInputEnabled(true);\n            binding.tabLayout.setVisibility(View.VISIBLE);\n            binding.fab.show();\n        } else {\n            binding.viewPager.setUserInputEnabled(false);\n            binding.tabLayout.setVisibility(View.GONE);\n        }\n\n        var tmp = new SparseArray<ModuleAdapter>(users.size());\n        var snapshot = adapters;\n        for (var user : users) {\n            if (snapshot.indexOfKey(user.id) >= 0) {\n                tmp.put(user.id, snapshot.get(user.id));\n            } else {\n                var adapter = new ModuleAdapter(user);\n                adapter.setHasStableIds(true);\n                tmp.put(user.id, adapter);\n            }\n        }\n        adapters = tmp;\n        forEachAdaptor(ModuleAdapter::refresh);\n        runOnUiThread(pagerAdapter::notifyDataSetChanged);\n        updateModuleSummary();\n    }\n\n    @Override\n    public void onRepoLoaded() {\n        forEachAdaptor(ModuleAdapter::refresh);\n    }\n\n    private void updateModuleSummary() {\n        var moduleCount = moduleUtil.getEnabledModulesCount();\n        runOnUiThread(() -> {\n            if (binding != null) {\n                binding.toolbar.setSubtitle(moduleCount == -1 ? getString(R.string.loading) : getResources().getQuantityString(R.plurals.modules_enabled_count, moduleCount, moduleCount));\n                binding.toolbarLayout.setSubtitle(binding.toolbar.getSubtitle());\n            }\n        });\n    }\n\n    void installModuleToUser(ModuleUtil.InstalledModule module, UserInfo user) {\n        new BlurBehindDialogBuilder(requireActivity(), R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons)\n                .setTitle(getString(R.string.install_to_user, user.name))\n                .setMessage(getString(R.string.install_to_user_message, module.getAppName(), user.name))\n                .setPositiveButton(android.R.string.ok, (dialog, which) ->\n                        runAsync(() -> {\n                            var success = ConfigManager.installExistingPackageAsUser(module.packageName, user.id);\n                            String text = success ?\n                                    getString(R.string.module_installed, module.getAppName(), user.name) :\n                                    getString(R.string.module_install_failed);\n                            showHint(text, false);\n                            if (success)\n                                moduleUtil.reloadSingleModule(module.packageName, user.id);\n                        }))\n                .setNegativeButton(android.R.string.cancel, null)\n                .show();\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    @Override\n    public boolean onContextItemSelected(@NonNull MenuItem item) {\n        if (selectedModule == null) {\n            return false;\n        }\n        int itemId = item.getItemId();\n        if (itemId == R.id.menu_launch) {\n            String packageName = selectedModule.packageName;\n            if (packageName == null) {\n                return false;\n            }\n            Intent intent = AppHelper.getSettingsIntent(packageName, selectedModule.userId);\n            if (intent != null) {\n                ConfigManager.startActivityAsUserWithFeature(intent, selectedModule.userId);\n            }\n            return true;\n        } else if (itemId == R.id.menu_other_app) {\n            var intent = new Intent(Intent.ACTION_SHOW_APP_INFO);\n            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, selectedModule.packageName);\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            ConfigManager.startActivityAsUserWithFeature(intent, selectedModule.userId);\n            return true;\n        } else if (itemId == R.id.menu_app_info) {\n            ConfigManager.startActivityAsUserWithFeature(new Intent(ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts(\"package\", selectedModule.packageName, null)), selectedModule.userId);\n            return true;\n        } else if (itemId == R.id.menu_uninstall) {\n            new BlurBehindDialogBuilder(requireActivity(), R.style.ThemeOverlay_MaterialAlertDialog_FullWidthButtons)\n                    .setIcon(selectedModule.app.loadIcon(pm))\n                    .setTitle(selectedModule.getAppName())\n                    .setMessage(R.string.module_uninstall_message)\n                    .setPositiveButton(android.R.string.ok, (dialog, which) ->\n                            runAsync(() -> {\n                                boolean success = ConfigManager.uninstallPackage(selectedModule.packageName, selectedModule.userId);\n                                String text = success ? getString(R.string.module_uninstalled, selectedModule.getAppName()) : getString(R.string.module_uninstall_failed);\n                                showHint(text, false);\n                                if (success)\n                                    moduleUtil.reloadSingleModule(selectedModule.packageName, selectedModule.userId);\n                            }))\n                    .setNegativeButton(android.R.string.cancel, null)\n                    .show();\n            return true;\n        } else if (itemId == R.id.menu_repo) {\n            var navController = getNavController();\n            navController.navigate(\n                    new Uri.Builder().scheme(\"lsposed\").authority(\"repo\").appendQueryParameter(\"modulePackageName\", selectedModule.packageName).build(),\n                    new NavOptions.Builder().setEnterAnim(R.anim.fragment_enter).setExitAnim(R.anim.fragment_exit).setPopEnterAnim(R.anim.fragment_enter_pop).setPopExitAnim(R.anim.fragment_exit_pop).setLaunchSingleTop(true).setPopUpTo(getNavController().getGraph().getStartDestinationId(), false, true).build());\n            return true;\n        } else if (itemId == R.id.menu_compile_speed) {\n            CompileDialogFragment.speed(getChildFragmentManager(), selectedModule.pkg.applicationInfo);\n        }\n        return super.onContextItemSelected(item);\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        moduleUtil.removeListener(this);\n        repoLoader.removeListener(this);\n        binding = null;\n    }\n\n    public static class ModuleListFragment extends Fragment {\n        public SwiperefreshRecyclerviewBinding binding;\n        private ModuleAdapter adapter;\n        private final RecyclerView.AdapterDataObserver observer = new RecyclerView.AdapterDataObserver() {\n            @Override\n            public void onChanged() {\n                binding.swipeRefreshLayout.setRefreshing(!adapter.isLoaded());\n            }\n        };\n\n        private final View.OnAttachStateChangeListener searchViewLocker = new View.OnAttachStateChangeListener() {\n            @Override\n            public void onViewAttachedToWindow(@NonNull View v) {\n                binding.recyclerView.setNestedScrollingEnabled(false);\n            }\n\n            @Override\n            public void onViewDetachedFromWindow(@NonNull View v) {\n                binding.recyclerView.setNestedScrollingEnabled(true);\n            }\n        };\n\n        @Nullable\n        @Override\n        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n            ModulesFragment fragment = (ModulesFragment) getParentFragment();\n            Bundle arguments = getArguments();\n            if (fragment == null || arguments == null) {\n                return null;\n            }\n            int userId = arguments.getInt(\"user_id\");\n            binding = SwiperefreshRecyclerviewBinding.inflate(getLayoutInflater(), container, false);\n            adapter = fragment.adapters.get(userId);\n            binding.recyclerView.setAdapter(adapter);\n            binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));\n            binding.swipeRefreshLayout.setOnRefreshListener(adapter::fullRefresh);\n            binding.swipeRefreshLayout.setProgressViewEndTarget(true, binding.swipeRefreshLayout.getProgressViewEndOffset());\n            RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);\n            adapter.registerAdapterDataObserver(observer);\n            return binding.getRoot();\n        }\n\n        void attachListeners() {\n            var parent = getParentFragment();\n            if (parent instanceof ModulesFragment moduleFragment) {\n                binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> moduleFragment.binding.appBar.setLifted(!top));\n                moduleFragment.binding.appBar.setLifted(!binding.recyclerView.getBorderViewDelegate().isShowingTopBorder());\n                moduleFragment.searchView.addOnAttachStateChangeListener(searchViewLocker);\n                binding.recyclerView.setNestedScrollingEnabled(moduleFragment.searchView.isIconified());\n                View.OnClickListener l = v -> {\n                    if (moduleFragment.searchView.isIconified()) {\n                        binding.recyclerView.smoothScrollToPosition(0);\n                        moduleFragment.binding.appBar.setExpanded(true, true);\n                    }\n                };\n                moduleFragment.binding.clickView.setOnClickListener(l);\n                moduleFragment.binding.toolbar.setOnClickListener(l);\n            }\n        }\n\n        void detachListeners() {\n            binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener(null);\n            var parent = getParentFragment();\n            if (parent instanceof ModulesFragment moduleFragment) {\n                moduleFragment.searchView.removeOnAttachStateChangeListener(searchViewLocker);\n                binding.recyclerView.setNestedScrollingEnabled(true);\n            }\n        }\n\n        @Override\n        public void onStart() {\n            super.onStart();\n            attachListeners();\n        }\n\n        @Override\n        public void onResume() {\n            super.onResume();\n            attachListeners();\n        }\n\n        @Override\n        public void onDestroyView() {\n            adapter.unregisterAdapterDataObserver(observer);\n            super.onDestroyView();\n        }\n\n        @Override\n        public void onPause() {\n            super.onPause();\n            detachListeners();\n        }\n\n        @Override\n        public void onStop() {\n            super.onStop();\n            detachListeners();\n        }\n    }\n\n    private class PagerAdapter extends FragmentStateAdapter {\n\n        public PagerAdapter(@NonNull Fragment fragment) {\n            super(fragment);\n        }\n\n        @NonNull\n        @Override\n        public Fragment createFragment(int position) {\n            Bundle bundle = new Bundle();\n            bundle.putInt(\"user_id\", adapters.keyAt(position));\n            Fragment fragment = new ModuleListFragment();\n            fragment.setArguments(bundle);\n            return fragment;\n        }\n\n        @Override\n        public int getItemCount() {\n            return adapters.size();\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return adapters.keyAt(position);\n        }\n\n        @Override\n        public boolean containsItem(long itemId) {\n            return adapters.indexOfKey((int) itemId) >= 0;\n        }\n    }\n\n    ModuleAdapter createPickModuleAdapter(UserInfo userInfo) {\n        return new ModuleAdapter(userInfo, true);\n    }\n\n    class ModuleAdapter extends EmptyStateRecyclerView.EmptyStateAdapter<ModuleAdapter.ViewHolder> implements Filterable {\n        private List<ModuleUtil.InstalledModule> searchList = new ArrayList<>();\n        private List<ModuleUtil.InstalledModule> showList = new ArrayList<>();\n        private final UserInfo user;\n        private final boolean isPick;\n        private boolean isLoaded;\n        private View.OnClickListener onPickListener;\n\n        ModuleAdapter(UserInfo user) {\n            this(user, false);\n        }\n\n        ModuleAdapter(UserInfo user, boolean isPick) {\n            this.user = user;\n            this.isPick = isPick;\n        }\n\n        public UserInfo getUser() {\n            return user;\n        }\n\n        @NonNull\n        @Override\n        public ModuleAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            return new ViewHolder(ItemModuleBinding.inflate(getLayoutInflater(), parent, false));\n        }\n\n        public boolean isPick() {\n            return isPick;\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull ModuleAdapter.ViewHolder holder, int position) {\n            ModuleUtil.InstalledModule item = showList.get(position);\n            String appName;\n            if (item.userId != 0) {\n                appName = String.format(LocaleDelegate.getDefaultLocale(), \"%s (%d)\", item.getAppName(), item.userId);\n            } else {\n                appName = item.getAppName();\n            }\n            holder.appName.setText(appName);\n            GlideApp.with(holder.appIcon)\n                    .load(item.getPackageInfo())\n                    .into(new CustomTarget<Drawable>() {\n                        @Override\n                        public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {\n                            holder.appIcon.setImageDrawable(resource);\n                        }\n\n                        @Override\n                        public void onLoadCleared(@Nullable Drawable placeholder) {\n\n                        }\n                    });\n            SpannableStringBuilder sb = new SpannableStringBuilder();\n            if (!item.getDescription().isEmpty()) {\n                sb.append(item.getDescription());\n            } else {\n                sb.append(getString(R.string.module_empty_description));\n            }\n            holder.appDescription.setText(sb);\n            holder.appDescription.setVisibility(View.VISIBLE);\n            sb = new SpannableStringBuilder();\n\n            int installXposedVersion = ConfigManager.getXposedApiVersion();\n            String warningText = null;\n            if (item.minVersion == 0) {\n                warningText = getString(R.string.no_min_version_specified);\n            } else if (installXposedVersion > 0 && item.minVersion > installXposedVersion) {\n                warningText = getString(R.string.warning_xposed_min_version, item.minVersion);\n            } else if (item.targetVersion > installXposedVersion) {\n                warningText = getString(R.string.warning_target_version_higher, item.targetVersion);\n            } else if (item.minVersion < ModuleUtil.MIN_MODULE_VERSION) {\n                warningText = getString(R.string.warning_min_version_too_low, item.minVersion, ModuleUtil.MIN_MODULE_VERSION);\n            } else if (item.isInstalledOnExternalStorage()) {\n                warningText = getString(R.string.warning_installed_on_external_storage);\n            }\n            if (warningText != null) {\n                sb.append(warningText);\n                final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(requireActivity().getTheme(), com.google.android.material.R.attr.colorError));\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                    final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create(\"sans-serif-medium\", Typeface.NORMAL));\n                    sb.setSpan(typefaceSpan, sb.length() - warningText.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                } else {\n                    final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);\n                    sb.setSpan(styleSpan, sb.length() - warningText.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n                sb.setSpan(foregroundColorSpan, sb.length() - warningText.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            }\n            var ver = repoLoader.getModuleLatestVersion(item.packageName);\n            if (ver != null && ver.upgradable(item.versionCode, item.versionName)) {\n                if (warningText != null) sb.append(\"\\n\");\n                String recommended = getString(R.string.update_available, ver.versionName);\n                sb.append(recommended);\n                final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(requireActivity().getTheme(), androidx.appcompat.R.attr.colorPrimary));\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                    final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create(\"sans-serif-medium\", Typeface.NORMAL));\n                    sb.setSpan(typefaceSpan, sb.length() - recommended.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                } else {\n                    final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);\n                    sb.setSpan(styleSpan, sb.length() - recommended.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n                sb.setSpan(foregroundColorSpan, sb.length() - recommended.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            }\n            if (sb.length() == 0) {\n                holder.hint.setVisibility(View.GONE);\n            } else {\n                holder.hint.setVisibility(View.VISIBLE);\n                holder.hint.setText(sb);\n            }\n\n            if (!isPick) {\n                holder.root.setAlpha(moduleUtil.isModuleEnabled(item.packageName) ? 1.0f : .5f);\n                holder.itemView.setOnClickListener(v -> {\n                    searchView.clearFocus();\n                    if (isLoaded()) {\n                        safeNavigate(ModulesFragmentDirections.actionModulesFragmentToAppListFragment(item.packageName, item.userId));\n                    }\n                });\n                holder.itemView.setOnLongClickListener(v -> {\n                    searchView.clearFocus();\n                    selectedModule = item;\n                    return false;\n                });\n                holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> {\n                    requireActivity().getMenuInflater().inflate(R.menu.context_menu_modules, menu);\n                    menu.setHeaderTitle(item.getAppName());\n                    Intent intent = AppHelper.getSettingsIntent(item.packageName, item.userId);\n                    if (intent == null) {\n                        menu.removeItem(R.id.menu_launch);\n                    }\n                    if (repoLoader.getOnlineModule(item.packageName) == null) {\n                        menu.removeItem(R.id.menu_repo);\n                    }\n                    if (item.userId == 0) {\n                        var users = ConfigManager.getUsers();\n                        if (users != null) {\n                            for (var user : users) {\n                                if (moduleUtil.getModule(item.packageName, user.id) == null) {\n                                    menu.add(1, user.id, 0, getString(R.string.install_to_user, user.name)).setOnMenuItemClickListener(i -> {\n                                        installModuleToUser(selectedModule, user);\n                                        return true;\n                                    });\n                                }\n                            }\n                        }\n                    }\n                });\n                holder.appVersion.setVisibility(View.VISIBLE);\n                holder.appVersion.setText(item.versionName);\n                holder.appVersion.setSelected(true);\n            } else {\n                holder.itemView.setTag(item);\n                holder.itemView.setOnClickListener(v -> {\n                    if (onPickListener != null) onPickListener.onClick(v);\n                });\n            }\n        }\n\n        @Override\n        public void onViewRecycled(@NonNull ViewHolder holder) {\n            holder.itemView.setTag(null);\n            super.onViewRecycled(holder);\n        }\n\n        @Override\n        public int getItemCount() {\n            return showList.size();\n        }\n\n        @Override\n        public long getItemId(int position) {\n            var module = showList.get(position);\n            return (module.packageName + \"!\" + module.userId).hashCode();\n        }\n\n        @Override\n        public Filter getFilter() {\n            return new ModuleAdapter.ApplicationFilter();\n        }\n\n        public void setOnPickListener(View.OnClickListener onPickListener) {\n            this.onPickListener = onPickListener;\n        }\n\n        public void refresh() {\n            runAsync(reloadModules);\n        }\n\n        public void fullRefresh() {\n            runAsync(() -> {\n                setLoaded(null, false);\n                moduleUtil.reloadInstalledModules();\n                refresh();\n            });\n        }\n\n        private final Runnable reloadModules = () -> {\n            var modules = moduleUtil.getModules();\n            if (modules == null) return;\n            Comparator<PackageInfo> cmp = AppHelper.getAppListComparator(0, pm);\n            setLoaded(null, false);\n            var tmpList = new ArrayList<ModuleUtil.InstalledModule>();\n            modules.values().parallelStream()\n                    .sorted((a, b) -> {\n                        boolean aChecked = moduleUtil.isModuleEnabled(a.packageName);\n                        boolean bChecked = moduleUtil.isModuleEnabled(b.packageName);\n                        if (aChecked == bChecked) {\n                            var c = cmp.compare(a.pkg, b.pkg);\n                            if (c == 0) {\n                                if (a.userId == getUser().id) return -1;\n                                if (b.userId == getUser().id) return 1;\n                                else return Integer.compare(a.userId, b.userId);\n                            }\n                            return c;\n                        } else if (aChecked) {\n                            return -1;\n                        } else {\n                            return 1;\n                        }\n                    }).forEachOrdered(new Consumer<>() {\n                        private final HashSet<String> uniquer = new HashSet<>();\n\n                        @Override\n                        public void accept(ModuleUtil.InstalledModule module) {\n                            if (isPick()) {\n                                if (!uniquer.contains(module.packageName)) {\n                                    uniquer.add(module.packageName);\n                                    if (module.userId != getUser().id)\n                                        tmpList.add(module);\n                                }\n                            } else if (module.userId == getUser().id) {\n                                tmpList.add(module);\n                            }\n                        }\n                    });\n            String queryStr = searchView != null ? searchView.getQuery().toString() : \"\";\n            searchList = tmpList;\n            runOnUiThread(() -> getFilter().filter(queryStr));\n        };\n\n        @SuppressLint(\"NotifyDataSetChanged\")\n        private void setLoaded(List<ModuleUtil.InstalledModule> list, boolean loaded) {\n            runOnUiThread(() -> {\n                if (list != null) showList = list;\n                isLoaded = loaded;\n                notifyDataSetChanged();\n            });\n        }\n\n        @Override\n        public boolean isLoaded() {\n            return isLoaded && moduleUtil.isModulesLoaded();\n        }\n\n        static class ViewHolder extends RecyclerView.ViewHolder {\n            ConstraintLayout root;\n            ImageView appIcon;\n            TextView appName;\n            TextView appDescription;\n            TextView appVersion;\n            TextView hint;\n            MaterialCheckBox checkBox;\n\n            ViewHolder(ItemModuleBinding binding) {\n                super(binding.getRoot());\n                root = binding.itemRoot;\n                appIcon = binding.appIcon;\n                appName = binding.appName;\n                appDescription = binding.description;\n                appVersion = binding.versionName;\n                hint = binding.hint;\n                checkBox = binding.checkbox;\n            }\n        }\n\n        class ApplicationFilter extends Filter {\n\n            private boolean lowercaseContains(String s, String filter) {\n                return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);\n            }\n\n            @Override\n            protected FilterResults performFiltering(CharSequence constraint) {\n                FilterResults filterResults = new FilterResults();\n                List<ModuleUtil.InstalledModule> filtered = new ArrayList<>();\n                String filter = constraint.toString().toLowerCase();\n                for (ModuleUtil.InstalledModule info : searchList) {\n                    if (lowercaseContains(info.getAppName(), filter) ||\n                            lowercaseContains(info.packageName, filter) ||\n                            lowercaseContains(info.getDescription(), filter)) {\n                        filtered.add(info);\n                    }\n                }\n                filterResults.values = filtered;\n                filterResults.count = filtered.size();\n                return filterResults;\n            }\n\n            @Override\n            protected void publishResults(CharSequence constraint, FilterResults results) {\n                //noinspection unchecked\n                setLoaded((List<ModuleUtil.InstalledModule>) results.values, true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/RecyclerViewDialogFragment.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.app.Dialog;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatDialogFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport org.lsposed.lspd.models.UserInfo;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.DialogTitleBinding;\nimport org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;\nimport org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;\nimport org.lsposed.manager.util.ModuleUtil;\n\npublic class RecyclerViewDialogFragment extends AppCompatDialogFragment {\n    @Override\n    @NonNull\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        var parent = getParentFragment();\n        var arguments = getArguments();\n        if (!(parent instanceof ModulesFragment) || arguments == null) {\n            throw new IllegalStateException();\n        }\n        var modulesFragment = (ModulesFragment) parent;\n        var user = (UserInfo) arguments.getParcelable(\"userInfo\");\n\n        var pickAdaptor = modulesFragment.createPickModuleAdapter(user);\n        var binding = SwiperefreshRecyclerviewBinding.inflate(LayoutInflater.from(requireActivity()), null, false);\n\n        binding.recyclerView.setAdapter(pickAdaptor);\n        binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));\n        pickAdaptor.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n            @Override\n            public void onChanged() {\n                binding.swipeRefreshLayout.setRefreshing(!pickAdaptor.isLoaded());\n            }\n        });\n        binding.swipeRefreshLayout.setProgressViewEndTarget(true, binding.swipeRefreshLayout.getProgressViewEndOffset());\n        binding.swipeRefreshLayout.setOnRefreshListener(pickAdaptor::fullRefresh);\n        pickAdaptor.refresh();\n        var title = DialogTitleBinding.inflate(getLayoutInflater()).getRoot();\n        title.setText(getString(R.string.install_to_user, user.name));\n        var dialog = new BlurBehindDialogBuilder(requireActivity(), R.style.ThemeOverlay_MaterialAlertDialog_FullWidthButtons)\n                .setCustomTitle(title)\n                .setView(binding.getRoot())\n                .setNegativeButton(android.R.string.cancel, null)\n                .create();\n        title.setOnClickListener(s -> binding.recyclerView.smoothScrollToPosition(0));\n        pickAdaptor.setOnPickListener(picked -> {\n            var module = (ModuleUtil.InstalledModule) picked.getTag();\n            modulesFragment.installModuleToUser(module, user);\n            dialog.dismiss();\n        });\n        onViewCreated(binding.getRoot(), savedInstanceState);\n        return dialog;\n    }\n\n    // prevent from overriding\n    public final void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.annotation.SuppressLint;\nimport android.content.res.Resources;\nimport android.graphics.Typeface;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.text.style.ForegroundColorSpan;\nimport android.text.style.StyleSpan;\nimport android.text.style.TypefaceSpan;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\nimport android.widget.Filter;\nimport android.widget.Filterable;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.view.MenuProvider;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.FragmentRepoBinding;\nimport org.lsposed.manager.databinding.ItemOnlinemoduleBinding;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.repo.model.OnlineModule;\nimport org.lsposed.manager.ui.widget.EmptyStateRecyclerView;\nimport org.lsposed.manager.util.ModuleUtil;\n\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\nimport rikka.core.util.LabelComparator;\nimport rikka.core.util.ResourceUtils;\nimport rikka.recyclerview.RecyclerViewKt;\n\npublic class RepoFragment extends BaseFragment implements RepoLoader.RepoListener, ModuleUtil.ModuleListener, MenuProvider {\n    protected FragmentRepoBinding binding;\n    protected SearchView searchView;\n    private SearchView.OnQueryTextListener mSearchListener;\n    private final Handler mHandler = new Handler(Looper.getMainLooper());\n    private boolean preLoadWebview = true;\n\n    private final RepoLoader repoLoader = RepoLoader.getInstance();\n    private final ModuleUtil moduleUtil = ModuleUtil.getInstance();\n    private RepoAdapter adapter;\n    private final RecyclerView.AdapterDataObserver observer = new RecyclerView.AdapterDataObserver() {\n        @Override\n        public void onChanged() {\n            binding.swipeRefreshLayout.setRefreshing(!adapter.isLoaded());\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        mSearchListener = new SearchView.OnQueryTextListener() {\n            @Override\n            public boolean onQueryTextSubmit(String query) {\n                adapter.getFilter().filter(query);\n                return false;\n            }\n\n            @Override\n            public boolean onQueryTextChange(String newText) {\n                adapter.getFilter().filter(newText);\n                return false;\n            }\n        };\n        super.onCreate(savedInstanceState);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        binding = FragmentRepoBinding.inflate(getLayoutInflater(), container, false);\n        binding.appBar.setLiftable(true);\n        binding.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setLifted(!top));\n        setupToolbar(binding.toolbar, binding.clickView, R.string.module_repo, R.menu.menu_repo);\n        binding.toolbar.setNavigationIcon(null);\n        adapter = new RepoAdapter();\n        adapter.setHasStableIds(true);\n        adapter.registerAdapterDataObserver(observer);\n        binding.recyclerView.setAdapter(adapter);\n        binding.recyclerView.setHasFixedSize(true);\n        binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));\n        RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);\n        binding.swipeRefreshLayout.setOnRefreshListener(adapter::fullRefresh);\n        binding.swipeRefreshLayout.setProgressViewEndTarget(true, binding.swipeRefreshLayout.getProgressViewEndOffset());\n        View.OnClickListener l = v -> {\n            if (searchView.isIconified()) {\n                binding.recyclerView.smoothScrollToPosition(0);\n                binding.appBar.setExpanded(true, true);\n            }\n        };\n        binding.toolbar.setOnClickListener(l);\n        binding.clickView.setOnClickListener(l);\n        repoLoader.addListener(this);\n        moduleUtil.addListener(this);\n        onRepoLoaded();\n        return binding.getRoot();\n    }\n\n    private void updateRepoSummary() {\n        final int[] count = new int[]{0};\n        HashSet<String> processedModules = new HashSet<>();\n        var modules = moduleUtil.getModules();\n        if (modules != null && repoLoader.isRepoLoaded()) {\n            modules.forEach((k, v) -> {\n                        if (!processedModules.contains(k.first)) {\n                            var ver = repoLoader.getModuleLatestVersion(k.first);\n                            if (ver != null && ver.upgradable(v.versionCode, v.versionName)) {\n                                ++count[0];\n                            }\n                            processedModules.add(k.first);\n                        }\n                    }\n            );\n        } else {\n            count[0] = -1;\n        }\n        runOnUiThread(() -> {\n            if (binding != null) {\n                if (count[0] > 0) {\n                    binding.toolbar.setSubtitle(getResources().getQuantityString(R.plurals.module_repo_upgradable, count[0], count[0]));\n                } else if (count[0] == 0) {\n                    binding.toolbar.setSubtitle(getResources().getString(R.string.module_repo_up_to_date));\n                } else {\n                    binding.toolbar.setSubtitle(getResources().getString(R.string.loading));\n                }\n                binding.toolbarLayout.setSubtitle(binding.toolbar.getSubtitle());\n            }\n        });\n    }\n\n    @Override\n    public void onPrepareMenu(Menu menu) {\n        searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();\n        if (searchView != null) {\n            searchView.setOnQueryTextListener(mSearchListener);\n            searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n                @Override\n                public void onViewAttachedToWindow(@NonNull View arg0) {\n                    binding.appBar.setExpanded(false, true);\n                    binding.recyclerView.setNestedScrollingEnabled(false);\n                }\n\n                @Override\n                public void onViewDetachedFromWindow(@NonNull View v) {\n                    binding.recyclerView.setNestedScrollingEnabled(true);\n                }\n            });\n            searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);\n        }\n        int sort = App.getPreferences().getInt(\"repo_sort\", 0);\n        if (sort == 0) {\n            menu.findItem(R.id.item_sort_by_name).setChecked(true);\n        } else if (sort == 1) {\n            menu.findItem(R.id.item_sort_by_update_time).setChecked(true);\n        }\n        menu.findItem(R.id.item_upgradable_first).setChecked(App.getPreferences().getBoolean(\"upgradable_first\", true));\n    }\n\n    @Override\n    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n\n        mHandler.removeCallbacksAndMessages(null);\n        repoLoader.removeListener(this);\n        moduleUtil.removeListener(this);\n        adapter.unregisterAdapterDataObserver(observer);\n        binding = null;\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        adapter.refresh();\n        if (preLoadWebview) {\n            mHandler.postDelayed(() -> new WebView(requireContext()), 500);\n            preLoadWebview = false;\n        }\n    }\n\n    @Override\n    public void onRepoLoaded() {\n        if (adapter != null) {\n            adapter.refresh();\n        }\n        updateRepoSummary();\n    }\n\n    @Override\n    public void onThrowable(Throwable t) {\n        showHint(getString(R.string.repo_load_failed, t.getLocalizedMessage()), true);\n        updateRepoSummary();\n    }\n\n    @Override\n    public void onModulesReloaded() {\n        updateRepoSummary();\n    }\n\n    @Override\n    public boolean onMenuItemSelected(@NonNull MenuItem item) {\n        int itemId = item.getItemId();\n        if (itemId == R.id.item_sort_by_name) {\n            item.setChecked(true);\n            App.getPreferences().edit().putInt(\"repo_sort\", 0).apply();\n            adapter.refresh();\n        } else if (itemId == R.id.item_sort_by_update_time) {\n            item.setChecked(true);\n            App.getPreferences().edit().putInt(\"repo_sort\", 1).apply();\n            adapter.refresh();\n        } else if (itemId == R.id.item_upgradable_first) {\n            item.setChecked(!item.isChecked());\n            App.getPreferences().edit().putBoolean(\"upgradable_first\", item.isChecked()).apply();\n            adapter.refresh();\n        } else {\n            return false;\n        }\n        return true;\n    }\n\n    private class RepoAdapter extends EmptyStateRecyclerView.EmptyStateAdapter<RepoAdapter.ViewHolder> implements Filterable {\n        private List<OnlineModule> fullList, showList;\n        private final LabelComparator labelComparator = new LabelComparator();\n        private boolean isLoaded = false;\n        private final Resources resources = App.getInstance().getResources();\n        private final String[] channels = resources.getStringArray(R.array.update_channel_values);\n        private String channel;\n        private final RepoLoader repoLoader = RepoLoader.getInstance();\n\n        RepoAdapter() {\n            fullList = showList = Collections.emptyList();\n        }\n\n        @NonNull\n        @Override\n        public RepoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            return new ViewHolder(ItemOnlinemoduleBinding.inflate(getLayoutInflater(), parent, false));\n        }\n\n        RepoLoader.ModuleVersion getUpgradableVer(OnlineModule module) {\n            ModuleUtil.InstalledModule installedModule = moduleUtil.getModule(module.getName());\n            if (installedModule != null) {\n                var ver = repoLoader.getModuleLatestVersion(installedModule.packageName);\n                if (ver != null && ver.upgradable(installedModule.versionCode, installedModule.versionName))\n                    return ver;\n            }\n            return null;\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull RepoAdapter.ViewHolder holder, int position) {\n            OnlineModule module = showList.get(position);\n            holder.appName.setText(module.getDescription());\n            holder.appPackageName.setText(module.getName());\n            Instant instant;\n            channel = App.getPreferences().getString(\"update_channel\", channels[0]);\n            var latestReleaseTime = repoLoader.getLatestReleaseTime(module.getName(), channel);\n            instant = Instant.parse(latestReleaseTime != null ? latestReleaseTime : module.getLatestReleaseTime());\n            var formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)\n                    .withLocale(App.getLocale()).withZone(ZoneId.systemDefault());\n            holder.publishedTime.setText(String.format(getString(R.string.module_repo_updated_time), formatter.format(instant)));\n            SpannableStringBuilder sb = new SpannableStringBuilder();\n\n            String summary = module.getSummary();\n            if (summary != null) {\n                sb.append(summary);\n            }\n            holder.appDescription.setVisibility(View.VISIBLE);\n            holder.appDescription.setText(sb);\n            sb = new SpannableStringBuilder();\n            var upgradableVer = getUpgradableVer(module);\n            if (upgradableVer != null) {\n                String hint = getString(R.string.update_available, upgradableVer.versionName);\n                sb.append(hint);\n                final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(requireActivity().getTheme(), com.google.android.material.R.attr.colorPrimary));\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                    final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create(\"sans-serif-medium\", Typeface.NORMAL));\n                    sb.setSpan(typefaceSpan, sb.length() - hint.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                } else {\n                    final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);\n                    sb.setSpan(styleSpan, sb.length() - hint.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n                sb.setSpan(foregroundColorSpan, sb.length() - hint.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            } else if (moduleUtil.getModule(module.getName()) != null) {\n                String installed = getString(R.string.installed);\n                sb.append(installed);\n                final StyleSpan styleSpan = new StyleSpan(Typeface.ITALIC);\n                sb.setSpan(styleSpan, sb.length() - installed.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(requireActivity().getTheme(), com.google.android.material.R.attr.colorSecondary));\n                sb.setSpan(foregroundColorSpan, sb.length() - installed.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            }\n            if (sb.length() > 0) {\n                holder.hint.setVisibility(View.VISIBLE);\n                holder.hint.setText(sb);\n            } else {\n                holder.hint.setVisibility(View.GONE);\n            }\n\n            holder.itemView.setOnClickListener(v -> {\n                searchView.clearFocus();\n                safeNavigate(RepoFragmentDirections.actionRepoFragmentToRepoItemFragment(module.getName()));\n            });\n            holder.itemView.setTooltipText(module.getDescription());\n        }\n\n        @Override\n        public int getItemCount() {\n            return showList.size();\n        }\n\n        @SuppressLint(\"NotifyDataSetChanged\")\n        private void setLoaded(List<OnlineModule> list, boolean isLoaded) {\n            runOnUiThread(() -> {\n                if (list != null) showList = list;\n                this.isLoaded = isLoaded;\n                notifyDataSetChanged();\n            });\n        }\n\n        public void setData(Collection<OnlineModule> modules) {\n            if (modules == null) return;\n            setLoaded(null, false);\n            channel = App.getPreferences().getString(\"update_channel\", channels[0]);\n            int sort = App.getPreferences().getInt(\"repo_sort\", 0);\n            boolean upgradableFirst = App.getPreferences().getBoolean(\"upgradable_first\", true);\n            ConcurrentHashMap<String, Boolean> upgradable = new ConcurrentHashMap<>();\n            fullList = modules.parallelStream().filter((onlineModule -> !onlineModule.isHide() && !(repoLoader.getReleases(onlineModule.getName()) != null && repoLoader.getReleases(onlineModule.getName()).isEmpty())))\n                    .sorted((a, b) -> {\n                        if (upgradableFirst) {\n                            var aUpgrade = upgradable.computeIfAbsent(a.getName(), n -> getUpgradableVer(a) != null);\n                            var bUpgrade = upgradable.computeIfAbsent(b.getName(), n -> getUpgradableVer(b) != null);\n                            if (aUpgrade && !bUpgrade) return -1;\n                            else if (!aUpgrade && bUpgrade) return 1;\n                        }\n                        if (sort == 0) {\n                            return labelComparator.compare(a.getDescription(), b.getDescription());\n                        } else {\n                            return Instant.parse(repoLoader.getLatestReleaseTime(b.getName(), channel)).compareTo(Instant.parse(repoLoader.getLatestReleaseTime(a.getName(), channel)));\n                        }\n                    }).collect(Collectors.toList());\n            String queryStr = searchView != null ? searchView.getQuery().toString() : \"\";\n            runOnUiThread(() -> getFilter().filter(queryStr));\n        }\n\n        public void fullRefresh() {\n            runAsync(() -> {\n                setLoaded(null, false);\n                repoLoader.loadRemoteData();\n                refresh();\n            });\n        }\n\n        public void refresh() {\n            runAsync(() -> adapter.setData(repoLoader.getOnlineModules()));\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return showList.get(position).getName().hashCode();\n        }\n\n        @Override\n        public Filter getFilter() {\n            return new RepoAdapter.ModuleFilter();\n        }\n\n        @Override\n        public boolean isLoaded() {\n            return isLoaded && repoLoader.isRepoLoaded();\n        }\n\n        static class ViewHolder extends RecyclerView.ViewHolder {\n            ConstraintLayout root;\n            TextView appName;\n            TextView appPackageName;\n            TextView appDescription;\n            TextView hint;\n            TextView publishedTime;\n\n            ViewHolder(ItemOnlinemoduleBinding binding) {\n                super(binding.getRoot());\n                root = binding.itemRoot;\n                appName = binding.appName;\n                appPackageName = binding.appPackageName;\n                appDescription = binding.description;\n                hint = binding.hint;\n                publishedTime = binding.publishedTime;\n            }\n        }\n\n        class ModuleFilter extends Filter {\n\n            private boolean lowercaseContains(String s, String filter) {\n                return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);\n            }\n\n            @Override\n            protected FilterResults performFiltering(CharSequence constraint) {\n                FilterResults filterResults = new FilterResults();\n                ArrayList<OnlineModule> filtered = new ArrayList<>();\n                String filter = constraint.toString().toLowerCase();\n                for (OnlineModule info : fullList) {\n                    if (lowercaseContains(info.getDescription(), filter) ||\n                            lowercaseContains(info.getName(), filter) ||\n                            lowercaseContains(info.getSummary(), filter)) {\n                        filtered.add(info);\n                    }\n                }\n                filterResults.values = filtered;\n                filterResults.count = filtered.size();\n                return filterResults;\n            }\n\n            @Override\n            protected void publishResults(CharSequence constraint, FilterResults results) {\n                //noinspection unchecked\n                setLoaded((List<OnlineModule>) results.values, true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.content.res.Resources;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.text.format.Formatter;\nimport android.text.style.ClickableSpan;\nimport android.text.style.ForegroundColorSpan;\nimport android.text.style.RelativeSizeSpan;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebSettings;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\nimport android.widget.ArrayAdapter;\nimport android.widget.ScrollView;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.MenuProvider;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.viewpager2.adapter.FragmentStateAdapter;\n\nimport com.google.android.material.button.MaterialButton;\nimport com.google.android.material.progressindicator.CircularProgressIndicator;\nimport com.google.android.material.tabs.TabLayout;\nimport com.google.android.material.tabs.TabLayoutMediator;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.FragmentPagerBinding;\nimport org.lsposed.manager.databinding.ItemRepoLoadmoreBinding;\nimport org.lsposed.manager.databinding.ItemRepoReadmeBinding;\nimport org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding;\nimport org.lsposed.manager.databinding.ItemRepoReleaseBinding;\nimport org.lsposed.manager.databinding.ItemRepoTitleDescriptionBinding;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.repo.model.Collaborator;\nimport org.lsposed.manager.repo.model.OnlineModule;\nimport org.lsposed.manager.repo.model.Release;\nimport org.lsposed.manager.repo.model.ReleaseAsset;\nimport org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;\nimport org.lsposed.manager.ui.widget.EmptyStateRecyclerView;\nimport org.lsposed.manager.ui.widget.LinkifyTextView;\nimport org.lsposed.manager.util.AccessibilityUtils;\nimport org.lsposed.manager.util.NavUtil;\nimport org.lsposed.manager.util.SimpleStatefulAdaptor;\nimport org.lsposed.manager.util.chrome.CustomTabsURLSpan;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport okhttp3.Headers;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport rikka.core.util.ResourceUtils;\nimport rikka.material.app.LocaleDelegate;\nimport rikka.recyclerview.RecyclerViewKt;\nimport rikka.widget.borderview.BorderView;\n\npublic class RepoItemFragment extends BaseFragment implements RepoLoader.RepoListener, MenuProvider {\n    FragmentPagerBinding binding;\n    OnlineModule module;\n    private ReleaseAdapter releaseAdapter;\n    private InformationAdapter informationAdapter;\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        binding = FragmentPagerBinding.inflate(getLayoutInflater(), container, false);\n        if (module == null) return binding.getRoot();\n        String modulePackageName = module.getName();\n        String moduleName = module.getDescription();\n        binding.appBar.setLiftable(true);\n        setupToolbar(binding.toolbar, binding.clickView, moduleName, R.menu.menu_repo_item);\n        binding.clickView.setTooltipText(moduleName);\n        binding.toolbar.setSubtitle(modulePackageName);\n        binding.viewPager.setAdapter(new PagerAdapter(this));\n        int[] titles = new int[]{R.string.module_readme, R.string.module_releases, R.string.module_information};\n\n        var isAnimationEnabled = AccessibilityUtils.isAnimationEnabled(requireContext().getContentResolver());\n        new TabLayoutMediator(\n            binding.tabLayout,\n            binding.viewPager,\n            // `autoRefresh = true` by default. Update the tabs automatically when the data set of the view pager's\n            // adapter changes.\n            true,\n            isAnimationEnabled,\n            (tab, position) -> tab.setText(titles[position])\n        ).attach();\n\n        binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {\n            ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);\n            int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();\n            if (tabLayoutWidth <= binding.getRoot().getWidth()) {\n                binding.tabLayout.setTabMode(TabLayout.MODE_FIXED);\n                binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);\n            }\n        });\n        binding.toolbar.setOnClickListener(v -> binding.appBar.setExpanded(true, true));\n        releaseAdapter = new ReleaseAdapter();\n        informationAdapter = new InformationAdapter();\n        RepoLoader.getInstance().addListener(this);\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        RepoLoader.getInstance().addListener(this);\n        super.onCreate(savedInstanceState);\n\n        String modulePackageName = getArguments() == null ? null : getArguments().getString(\"modulePackageName\");\n        module = RepoLoader.getInstance().getOnlineModule(modulePackageName);\n        if (module == null) {\n            if (!safeNavigate(R.id.action_repo_item_fragment_to_repo_fragment)) {\n                safeNavigate(R.id.repo_nav);\n            }\n        }\n    }\n\n    private void renderGithubMarkdown(WebView view, @Nullable String text) {\n        try {\n            view.setBackgroundColor(Color.TRANSPARENT);\n            var setting = view.getSettings();\n            setting.setOffscreenPreRaster(true);\n            setting.setDomStorageEnabled(true);\n            setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);\n            setting.setAllowContentAccess(false);\n            setting.setAllowFileAccessFromFileURLs(true);\n            setting.setAllowFileAccess(false);\n            setting.setGeolocationEnabled(false);\n            setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);\n            setting.setTextZoom(80);\n            String body;\n            String direction;\n            if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {\n                direction = \"rtl\";\n            } else {\n                direction = \"ltr\";\n            }\n            if (text == null) {\n                text = \"<center>\" + App.getInstance().getString(R.string.list_empty) + \"</center>\";\n            }\n            if (ResourceUtils.isNightMode(getResources().getConfiguration())) {\n                body = App.HTML_TEMPLATE_DARK.get().replace(\"@dir@\", direction).replace(\"@body@\", text);\n            } else {\n                body = App.HTML_TEMPLATE.get().replace(\"@dir@\", direction).replace(\"@body@\", text);\n            }\n            view.setWebViewClient(new WebViewClient() {\n                @Override\n                public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n                    NavUtil.startURL(requireActivity(), request.getUrl());\n                    return true;\n                }\n\n                @Nullable\n                @Override\n                public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\n                    if (!request.getUrl().getScheme().startsWith(\"http\")) return null;\n                    var client = App.getOkHttpClient();\n                    var call = client.newCall(\n                            new Request.Builder()\n                                    .url(request.getUrl().toString())\n                                    .method(request.getMethod(), null)\n                                    .headers(Headers.of(request.getRequestHeaders()))\n                                    .build());\n                    try {\n                        Response reply = call.execute();\n                        var header = reply.header(\"content-type\", \"image/*;charset=utf-8\");\n                        String[] contentTypes = new String[0];\n                        if (header != null) {\n                            contentTypes = header.split(\";\\\\s*\");\n                        }\n                        var mimeType = contentTypes.length > 0 ? contentTypes[0] : \"image/*\";\n                        var charset = contentTypes.length > 1 ? contentTypes[1].split(\"=\\\\s*\")[1] : \"utf-8\";\n                        var body = reply.body();\n                        if (body == null) return null;\n                        return new WebResourceResponse(\n                                mimeType,\n                                charset,\n                                body.byteStream()\n                        );\n                    } catch (Throwable e) {\n                        return new WebResourceResponse(\"text/html\", \"utf-8\", new ByteArrayInputStream(Log.getStackTraceString(e).getBytes(StandardCharsets.UTF_8)));\n                    }\n                }\n            });\n            view.loadDataWithBaseURL(\"https://github.com\", body, \"text/html\",\n                    StandardCharsets.UTF_8.name(), null);\n        } catch (Throwable e) {\n            Log.e(App.TAG, \"render readme\", e);\n        }\n    }\n\n    @Override\n    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n\n    }\n\n    @Override\n    public boolean onMenuItemSelected(@NonNull MenuItem item) {\n        int id = item.getItemId();\n        if (id == R.id.menu_open_in_browser) {\n            NavUtil.startURL(requireActivity(), \"https://modules.lsposed.org/module/\" + module.getName());\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        RepoLoader.getInstance().removeListener(this);\n        binding = null;\n    }\n\n    @Override\n    public void onModuleReleasesLoaded(OnlineModule module) {\n        this.module = module;\n        var repoLoader = RepoLoader.getInstance();\n        if (releaseAdapter != null) {\n            runAsync(releaseAdapter::loadItems);\n        }\n        if ((repoLoader.getReleases(module.getName()) != null ? repoLoader.getReleases(module.getName()).size() : 1) == 1) {\n            showHint(R.string.module_release_no_more, true);\n        }\n    }\n\n    @Override\n    public void onThrowable(Throwable t) {\n        if (releaseAdapter != null) {\n            runAsync(releaseAdapter::loadItems);\n        }\n        showHint(getString(R.string.repo_load_failed, t.getLocalizedMessage()), true);\n    }\n\n    private class InformationAdapter extends SimpleStatefulAdaptor<InformationAdapter.ViewHolder> {\n\n        private int rowCount = 0;\n        private int homepageRow = -1;\n        private int collaboratorsRow = -1;\n        private int sourceUrlRow = -1;\n\n        public InformationAdapter() {\n            if (!TextUtils.isEmpty(module.getHomepageUrl())) {\n                homepageRow = rowCount++;\n            }\n            if (module.getCollaborators() != null && !module.getCollaborators().isEmpty()) {\n                collaboratorsRow = rowCount++;\n            }\n            if (!TextUtils.isEmpty(module.getSourceUrl())) {\n                sourceUrlRow = rowCount++;\n            }\n        }\n\n        @NonNull\n        @Override\n        public InformationAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            return new ViewHolder(ItemRepoTitleDescriptionBinding.inflate(getLayoutInflater(), parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull InformationAdapter.ViewHolder holder, int position) {\n            if (position == homepageRow) {\n                holder.title.setText(R.string.module_information_homepage);\n                holder.description.setText(module.getHomepageUrl());\n            } else if (position == collaboratorsRow) {\n                List<Collaborator> collaborators = module.getCollaborators();\n                if (collaborators == null) return;\n                holder.title.setText(R.string.module_information_collaborators);\n                SpannableStringBuilder sb = new SpannableStringBuilder();\n                ListIterator<Collaborator> iterator = collaborators.listIterator();\n                while (iterator.hasNext()) {\n                    Collaborator collaborator = iterator.next();\n                    var collaboratorLogin = collaborator.getLogin();\n                    if (collaboratorLogin == null) continue;\n                    String name = collaborator.getName() == null ? collaboratorLogin : collaborator.getName();\n                    sb.append(name);\n                    CustomTabsURLSpan span = new CustomTabsURLSpan(requireActivity(), String.format(\"https://github.com/%s\", collaborator.getLogin()));\n                    sb.setSpan(span, sb.length() - name.length(), sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n                    if (iterator.hasNext()) {\n                        sb.append(\", \");\n                    }\n                }\n                holder.description.setText(sb);\n            } else if (position == sourceUrlRow) {\n                holder.title.setText(R.string.module_information_source_url);\n                holder.description.setText(module.getSourceUrl());\n            }\n            holder.itemView.setOnClickListener(v -> {\n                if (position == homepageRow) {\n                    NavUtil.startURL(requireActivity(), module.getHomepageUrl());\n                } else if (position == collaboratorsRow) {\n                    ClickableSpan span = holder.description.getCurrentSpan();\n                    holder.description.clearCurrentSpan();\n\n                    if (span instanceof CustomTabsURLSpan) {\n                        span.onClick(v);\n                    }\n                } else if (position == sourceUrlRow) {\n                    NavUtil.startURL(requireActivity(), module.getSourceUrl());\n                }\n            });\n        }\n\n        @Override\n        public int getItemCount() {\n            return rowCount;\n        }\n\n        class ViewHolder extends RecyclerView.ViewHolder {\n            TextView title;\n            LinkifyTextView description;\n\n            public ViewHolder(ItemRepoTitleDescriptionBinding binding) {\n                super(binding.getRoot());\n                title = binding.title;\n                description = binding.description;\n            }\n        }\n    }\n\n    public static class DownloadDialog extends DialogFragment {\n        @NonNull\n        @Override\n        public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {\n            var args = getArguments();\n            if (args == null) throw new IllegalArgumentException();\n            return new BlurBehindDialogBuilder(requireActivity(), R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons)\n                    .setTitle(R.string.module_release_view_assets)\n                    .setPositiveButton(android.R.string.cancel, null)\n                    .setAdapter(new ArrayAdapter<>(requireActivity(), R.layout.dialog_item, args.getCharSequenceArray(\"names\")),\n                            (dialog, which) -> NavUtil.startURL(requireActivity(), args.getStringArrayList(\"urls\").get(which)))\n                    .create();\n        }\n\n        static void create(Activity activity, FragmentManager fm, List<ReleaseAsset> assets) {\n            var f = new DownloadDialog();\n            var bundle = new Bundle();\n\n            var displayNames = new CharSequence[assets.size()];\n            for (int i = 0; i < assets.size(); i++) {\n                var sb = new SpannableStringBuilder(assets.get(i).getName());\n                var count = assets.get(i).getDownloadCount();\n                var countStr = activity.getResources().getQuantityString(R.plurals.module_release_assets_download_count, count, count);\n                var sizeStr = Formatter.formatShortFileSize(activity, assets.get(i).getSize());\n                sb.append('\\n').append(sizeStr).append('/').append(countStr);\n                final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(activity.getTheme(), android.R.attr.textColorSecondary));\n                final RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(0.8f);\n                sb.setSpan(foregroundColorSpan, sb.length() - sizeStr.length() - countStr.length() - 1, sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n                sb.setSpan(relativeSizeSpan, sb.length() - sizeStr.length() - countStr.length() - 1, sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);\n                displayNames[i] = sb;\n            }\n            bundle.putCharSequenceArray(\"names\", displayNames);\n            bundle.putStringArrayList(\"urls\", assets.stream().map(ReleaseAsset::getDownloadUrl).collect(Collectors.toCollection(ArrayList::new)));\n            f.setArguments(bundle);\n            f.show(fm, \"download\");\n        }\n    }\n\n    private class ReleaseAdapter extends EmptyStateRecyclerView.EmptyStateAdapter<ReleaseAdapter.ViewHolder> {\n        private List<Release> items = new ArrayList<>();\n        private final Resources resources = App.getInstance().getResources();\n\n        public ReleaseAdapter() {\n            runAsync(this::loadItems);\n        }\n\n        @SuppressLint(\"NotifyDataSetChanged\")\n        public void loadItems() {\n            var channels = resources.getStringArray(R.array.update_channel_values);\n            var channel = App.getPreferences().getString(\"update_channel\", channels[0]);\n            var releases = RepoLoader.getInstance().getReleases(module.getName());\n            if (releases == null) releases = module.getReleases();\n            List<Release> tmpList;\n            if (channel.equals(channels[0])) {\n                tmpList = releases != null ? releases.parallelStream().filter(t -> {\n                    if (Boolean.TRUE.equals(t.getIsPrerelease())) return false;\n                    var name = t.getName() != null ? t.getName().toLowerCase(LocaleDelegate.getDefaultLocale()) : null;\n                    return !(name != null && name.startsWith(\"snapshot\")) && !(name != null && name.startsWith(\"nightly\"));\n                }).collect(Collectors.toList()) : null;\n            } else if (channel.equals(channels[1])) {\n                tmpList = releases != null ? releases.parallelStream().filter(t -> {\n                    var name = t.getName() != null ? t.getName().toLowerCase(LocaleDelegate.getDefaultLocale()) : null;\n                    return !(name != null && name.startsWith(\"snapshot\")) && !(name != null && name.startsWith(\"nightly\"));\n                }).collect(Collectors.toList()) : null;\n            } else tmpList = releases;\n            runOnUiThread(() -> {\n                items = tmpList;\n                notifyDataSetChanged();\n            });\n        }\n\n        @NonNull\n        @Override\n        public ReleaseAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n            if (viewType == 0) {\n                return new ReleaseViewHolder(ItemRepoReleaseBinding.inflate(getLayoutInflater(), parent, false));\n            } else {\n                return new LoadmoreViewHolder(ItemRepoLoadmoreBinding.inflate(getLayoutInflater(), parent, false));\n            }\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull ReleaseAdapter.ViewHolder holder, int position) {\n            if (holder.getItemViewType() == 1) {\n                holder.progress.setVisibility(View.GONE);\n                holder.title.setVisibility(View.VISIBLE);\n                holder.itemView.setOnClickListener(v -> {\n                    if (holder.progress.getVisibility() == View.GONE) {\n                        holder.title.setVisibility(View.GONE);\n                        holder.progress.show();\n                        RepoLoader.getInstance().loadRemoteReleases(module.getName());\n                    }\n                });\n            } else {\n                Release release = items.get(position);\n                holder.title.setText(release.getName());\n                var instant = Instant.parse(release.getPublishedAt());\n                var formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)\n                        .withLocale(App.getLocale()).withZone(ZoneId.systemDefault());\n                holder.publishedTime.setText(String.format(getString(R.string.module_repo_published_time), formatter.format(instant)));\n                renderGithubMarkdown(holder.description, release.getDescriptionHTML());\n                holder.openInBrowser.setOnClickListener(v -> NavUtil.startURL(requireActivity(), release.getUrl()));\n                List<ReleaseAsset> assets = release.getReleaseAssets();\n                if (assets != null && !assets.isEmpty()) {\n                    holder.viewAssets.setOnClickListener(v -> DownloadDialog.create(requireActivity(), getParentFragmentManager(), assets));\n                } else {\n                    holder.viewAssets.setVisibility(View.GONE);\n                }\n            }\n        }\n\n        @Override\n        public int getItemCount() {\n            return items.size() + (module.releasesLoaded ? 0 : 1);\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return !module.releasesLoaded && position == getItemCount() - 1 ? 1 : 0;\n        }\n\n        @Override\n        public boolean isLoaded() {\n            return module.releasesLoaded;\n        }\n\n        class ViewHolder extends RecyclerView.ViewHolder {\n            TextView title;\n            TextView publishedTime;\n            WebView description;\n            MaterialButton openInBrowser;\n            MaterialButton viewAssets;\n            CircularProgressIndicator progress;\n\n            public ViewHolder(@NonNull View itemView) {\n                super(itemView);\n            }\n        }\n\n        class ReleaseViewHolder extends ReleaseAdapter.ViewHolder {\n            public ReleaseViewHolder(ItemRepoReleaseBinding binding) {\n                super(binding.getRoot());\n                title = binding.title;\n                publishedTime = binding.publishedTime;\n                description = binding.description;\n                openInBrowser = binding.openInBrowser;\n                viewAssets = binding.viewAssets;\n            }\n        }\n\n        class LoadmoreViewHolder extends ReleaseAdapter.ViewHolder {\n            public LoadmoreViewHolder(ItemRepoLoadmoreBinding binding) {\n                super(binding.getRoot());\n                title = binding.title;\n                progress = binding.progress;\n            }\n        }\n    }\n\n    private static class PagerAdapter extends FragmentStateAdapter {\n\n        public PagerAdapter(@NonNull Fragment fragment) {\n            super(fragment);\n        }\n\n        @NonNull\n        @Override\n        public Fragment createFragment(int position) {\n            Bundle bundle = new Bundle();\n            bundle.putInt(\"position\", position);\n            Fragment f;\n            if (position == 0) {\n                f = new ReadmeFragment();\n            } else if (position == 1) {\n                f = new RecyclerviewFragment();\n            } else {\n                f = new RecyclerviewFragment();\n            }\n            f.setArguments(bundle);\n            return f;\n        }\n\n        @Override\n        public int getItemCount() {\n            return 3;\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return position == 0 ? 0 : 1;\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return position;\n        }\n    }\n\n    public static abstract class BorderFragment extends BaseFragment {\n        BorderView borderView;\n\n        void attachListeners() {\n            var parent = getParentFragment();\n            if (parent instanceof RepoItemFragment) {\n                var repoItemFragment = (RepoItemFragment) parent;\n                borderView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> repoItemFragment.binding.appBar.setLifted(!top));\n                repoItemFragment.binding.appBar.setLifted(!borderView.getBorderViewDelegate().isShowingTopBorder());\n                repoItemFragment.binding.toolbar.setOnClickListener(v -> {\n                    repoItemFragment.binding.appBar.setExpanded(true, true);\n                    scrollToTop();\n                });\n            }\n        }\n\n        abstract void scrollToTop();\n\n        void detachListeners() {\n            borderView.getBorderViewDelegate().setBorderVisibilityChangedListener(null);\n        }\n\n        @Override\n        public void onResume() {\n            super.onResume();\n            attachListeners();\n        }\n\n        @Override\n        public void onStart() {\n            super.onStart();\n            attachListeners();\n        }\n\n        @Override\n        public void onStop() {\n            super.onStop();\n            detachListeners();\n        }\n\n        @Override\n        public void onPause() {\n            super.onPause();\n            detachListeners();\n        }\n    }\n\n    public static class ReadmeFragment extends BorderFragment {\n        ItemRepoReadmeBinding binding;\n\n        @Nullable\n        @Override\n        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n            var parent = getParentFragment();\n            if (!(parent instanceof RepoItemFragment)) {\n                if (!safeNavigate(R.id.action_repo_item_fragment_to_repo_fragment)) {\n                    safeNavigate(R.id.repo_nav);\n                }\n                return null;\n            }\n            var repoItemFragment = (RepoItemFragment) parent;\n            binding = ItemRepoReadmeBinding.inflate(getLayoutInflater(), container, false);\n            repoItemFragment.renderGithubMarkdown(binding.readme, repoItemFragment.module.getReadmeHTML());\n            borderView = binding.scrollView;\n            return binding.getRoot();\n        }\n\n        @Override\n        void scrollToTop() {\n            binding.scrollView.fullScroll(ScrollView.FOCUS_UP);\n        }\n    }\n\n    public static class RecyclerviewFragment extends BorderFragment {\n        ItemRepoRecyclerviewBinding binding;\n        RecyclerView.Adapter<?> adapter;\n\n        @Override\n        void scrollToTop() {\n            binding.recyclerView.smoothScrollToPosition(0);\n        }\n\n        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n            var arguments = getArguments();\n            var parent = getParentFragment();\n            if (arguments == null || !(parent instanceof RepoItemFragment)) {\n                if (!safeNavigate(R.id.action_repo_item_fragment_to_repo_fragment)) {\n                    safeNavigate(R.id.repo_nav);\n                }\n                return null;\n            }\n            var repoItemFragment = (RepoItemFragment) parent;\n            var position = arguments.getInt(\"position\", 0);\n            if (position == 1)\n                adapter = repoItemFragment.releaseAdapter;\n            else if (position == 2)\n                adapter = repoItemFragment.informationAdapter;\n            else return null;\n            binding = ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), container, false);\n            binding.recyclerView.setAdapter(adapter);\n            binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));\n            RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);\n            borderView = binding.recyclerView;\n            return binding.getRoot();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.fragment;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.provider.Settings;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatDelegate;\nimport androidx.core.text.HtmlCompat;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.color.DynamicColors;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.BuildConfig;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.databinding.FragmentSettingsBinding;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.ui.activity.MainActivity;\nimport org.lsposed.manager.util.BackupUtils;\nimport org.lsposed.manager.util.CloudflareDNS;\nimport org.lsposed.manager.util.LangList;\nimport org.lsposed.manager.util.NavUtil;\nimport org.lsposed.manager.util.ShortcutUtil;\nimport org.lsposed.manager.util.ThemeUtil;\n\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Locale;\n\nimport rikka.core.util.ResourceUtils;\nimport rikka.material.app.LocaleDelegate;\nimport rikka.material.preference.MaterialSwitchPreference;\nimport rikka.preference.SimpleMenuPreference;\nimport rikka.recyclerview.RecyclerViewKt;\nimport rikka.widget.borderview.BorderRecyclerView;\n\npublic class SettingsFragment extends BaseFragment {\n    FragmentSettingsBinding binding;\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        binding = FragmentSettingsBinding.inflate(inflater, container, false);\n        binding.appBar.setLiftable(true);\n        setupToolbar(binding.toolbar, binding.clickView, R.string.Settings);\n        binding.toolbar.setNavigationIcon(null);\n        if (savedInstanceState == null) {\n            getChildFragmentManager().beginTransaction().add(R.id.setting_container, new PreferenceFragment()).commitNow();\n        }\n        if (ConfigManager.isBinderAlive()) {\n            binding.toolbar.setSubtitle(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%d) - %s\", ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode(), ConfigManager.getApi()));\n        } else {\n            binding.toolbar.setSubtitle(String.format(LocaleDelegate.getDefaultLocale(), \"%s (%d) - %s\", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, getString(R.string.not_installed)));\n        }\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n\n        binding = null;\n    }\n\n    public static class PreferenceFragment extends PreferenceFragmentCompat {\n        private SettingsFragment parentFragment;\n\n        ActivityResultLauncher<String> backupLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(\"application/gzip\"), uri -> {\n            if (uri == null || parentFragment == null) return;\n            parentFragment.runAsync(() -> {\n                try {\n                    BackupUtils.backup(uri);\n                } catch (Exception e) {\n                    var text = App.getInstance().getString(R.string.settings_backup_failed2, e.getMessage());\n                    parentFragment.showHint(text, false);\n                }\n            });\n        });\n        ActivityResultLauncher<String[]> restoreLauncher = registerForActivityResult(new ActivityResultContracts.OpenDocument(), uri -> {\n            if (uri == null || parentFragment == null) return;\n            parentFragment.runAsync(() -> {\n                try {\n                    BackupUtils.restore(uri);\n                } catch (Exception e) {\n                    var text = App.getInstance().getString(R.string.settings_restore_failed2, e.getMessage());\n                    parentFragment.showHint(text, false);\n                }\n            });\n        });\n\n        @Override\n        public void onAttach(@NonNull Context context) {\n            super.onAttach(context);\n\n            parentFragment = (SettingsFragment) requireParentFragment();\n        }\n\n        @Override\n        public void onDetach() {\n            super.onDetach();\n\n            parentFragment = null;\n        }\n\n        private boolean setNotificationPreferenceEnabled(MaterialSwitchPreference notificationPreference, boolean preferenceEnabled) {\n            var notificationEnabled = ConfigManager.enableStatusNotification();\n            if (notificationPreference != null) {\n                notificationPreference.setEnabled(!notificationEnabled || preferenceEnabled);\n                notificationPreference.setSummaryOn(preferenceEnabled ?\n                        notificationPreference.getContext().getString(R.string.settings_enable_status_notification_summary) :\n                        notificationPreference.getContext().getString(R.string.settings_enable_status_notification_summary) + \"\\n\" +\n                                notificationPreference.getContext().getString(R.string.disable_status_notification_error));\n            }\n            return notificationEnabled;\n        }\n\n        @Override\n        public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {\n            final String SYSTEM = \"SYSTEM\";\n\n            addPreferencesFromResource(R.xml.prefs);\n\n            boolean installed = ConfigManager.isBinderAlive();\n            MaterialSwitchPreference prefVerboseLogs = findPreference(\"disable_verbose_log\");\n            if (prefVerboseLogs != null) {\n                prefVerboseLogs.setEnabled(!BuildConfig.DEBUG && installed);\n                if (BuildConfig.DEBUG) ConfigManager.setVerboseLogEnabled(false);\n                prefVerboseLogs.setChecked(!installed || !ConfigManager.isVerboseLogEnabled());\n                prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setVerboseLogEnabled(!(boolean) newValue));\n            }\n\n            MaterialSwitchPreference prefLogWatchDog = findPreference(\"enable_log_watchdog\");\n            if (prefLogWatchDog != null) {\n                prefLogWatchDog.setEnabled(!BuildConfig.DEBUG && installed);\n                if (BuildConfig.DEBUG) ConfigManager.setLogWatchdog(true);\n                prefLogWatchDog.setChecked(!installed || ConfigManager.isLogWatchdogEnabled());\n                prefLogWatchDog.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setLogWatchdog((boolean) newValue));\n            }\n\n            MaterialSwitchPreference prefDexObfuscate = findPreference(\"enable_dex_obfuscate\");\n            if (prefDexObfuscate != null) {\n                prefDexObfuscate.setEnabled(installed);\n                prefDexObfuscate.setChecked(!installed || ConfigManager.isDexObfuscateEnabled());\n                prefDexObfuscate.setOnPreferenceChangeListener((preference, newValue) -> {\n                    parentFragment.showHint(R.string.reboot_required, true, R.string.reboot, v -> ConfigManager.reboot());\n                    return ConfigManager.setDexObfuscateEnabled((boolean) newValue);\n                });\n            }\n\n            MaterialSwitchPreference notificationPreference = findPreference(\"enable_status_notification\");\n            if (notificationPreference != null) {\n                notificationPreference.setVisible(installed);\n                if (installed) {\n                    notificationPreference.setChecked(setNotificationPreferenceEnabled(notificationPreference, !App.isParasitic || ShortcutUtil.isLaunchShortcutPinned()));\n                }\n                notificationPreference.setOnPreferenceChangeListener((p, v) -> {\n                    var succeeded = ConfigManager.setEnableStatusNotification((boolean) v);\n                    if ((boolean) v && App.isParasitic && !ShortcutUtil.isLaunchShortcutPinned()) {\n                        setNotificationPreferenceEnabled(notificationPreference, false);\n                    }\n                    return succeeded;\n                });\n            }\n\n            Preference shortcut = findPreference(\"add_shortcut\");\n            if (shortcut != null) {\n                shortcut.setVisible(App.isParasitic);\n                if (!ShortcutUtil.isRequestPinShortcutSupported(requireContext())) {\n                    shortcut.setEnabled(false);\n                    shortcut.setSummary(R.string.settings_unsupported_pin_shortcut_summary);\n                }\n                shortcut.setOnPreferenceClickListener(preference -> {\n                    if (!ShortcutUtil.requestPinLaunchShortcut(() -> {\n                        setNotificationPreferenceEnabled(notificationPreference, true);\n                        App.getPreferences().edit().putBoolean(\"never_show_welcome\", true).apply();\n                        parentFragment.showHint(R.string.settings_shortcut_pinned_hint, false);\n                    })) {\n                        parentFragment.showHint(R.string.settings_unsupported_pin_shortcut_summary, true);\n                    }\n                    return true;\n                });\n            }\n\n            Preference backup = findPreference(\"backup\");\n            if (backup != null) {\n                backup.setEnabled(installed);\n                backup.setOnPreferenceClickListener(preference -> {\n                    LocalDateTime now = LocalDateTime.now();\n                    try {\n                        backupLauncher.launch(String.format(LocaleDelegate.getDefaultLocale(), \"LSPosed_%s.lsp\", now.toString()));\n                        return true;\n                    } catch (ActivityNotFoundException e) {\n                        parentFragment.showHint(R.string.enable_documentui, true);\n                        return false;\n                    }\n                });\n            }\n\n            Preference restore = findPreference(\"restore\");\n            if (restore != null) {\n                restore.setEnabled(installed);\n                restore.setOnPreferenceClickListener(preference -> {\n                    try {\n                        restoreLauncher.launch(new String[]{\"*/*\"});\n                        return true;\n                    } catch (ActivityNotFoundException e) {\n                        parentFragment.showHint(R.string.enable_documentui, true);\n                        return false;\n                    }\n                });\n            }\n\n            Preference theme = findPreference(\"dark_theme\");\n            if (theme != null) {\n                theme.setOnPreferenceChangeListener((preference, newValue) -> {\n                    if (!App.getPreferences().getString(\"dark_theme\", ThemeUtil.MODE_NIGHT_FOLLOW_SYSTEM).equals(newValue)) {\n                        AppCompatDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme((String) newValue));\n                    }\n                    return true;\n                });\n            }\n\n            Preference black_dark_theme = findPreference(\"black_dark_theme\");\n            if (black_dark_theme != null) {\n                black_dark_theme.setOnPreferenceChangeListener((preference, newValue) -> {\n                    MainActivity activity = (MainActivity) getActivity();\n                    if (activity != null && ResourceUtils.isNightMode(getResources().getConfiguration())) {\n                        activity.restart();\n                    }\n                    return true;\n                });\n            }\n\n            Preference primary_color = findPreference(\"theme_color\");\n            if (primary_color != null) {\n                primary_color.setOnPreferenceChangeListener((preference, newValue) -> {\n                    MainActivity activity = (MainActivity) getActivity();\n                    if (activity != null) {\n                        activity.restart();\n                    }\n                    return true;\n                });\n            }\n\n            MaterialSwitchPreference prefShowHiddenIcons = findPreference(\"show_hidden_icon_apps_enabled\");\n            if (prefShowHiddenIcons != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                if (ConfigManager.isBinderAlive()) {\n                    prefShowHiddenIcons.setEnabled(true);\n                    prefShowHiddenIcons.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setHiddenIcon(!(boolean) newValue));\n                }\n                prefShowHiddenIcons.setChecked(Settings.Global.getInt(requireActivity().getContentResolver(), \"show_hidden_icon_apps_enabled\", 1) != 0);\n            }\n\n            MaterialSwitchPreference prefFollowSystemAccent = findPreference(\"follow_system_accent\");\n            if (prefFollowSystemAccent != null && DynamicColors.isDynamicColorAvailable()) {\n                if (primary_color != null) {\n                    primary_color.setVisible(!prefFollowSystemAccent.isChecked());\n                }\n                prefFollowSystemAccent.setVisible(true);\n                prefFollowSystemAccent.setOnPreferenceChangeListener((preference, newValue) -> {\n                    MainActivity activity = (MainActivity) getActivity();\n                    if (activity != null) {\n                        activity.restart();\n                    }\n                    return true;\n                });\n            }\n\n            MaterialSwitchPreference prefDoH = findPreference(\"doh\");\n            if (prefDoH != null) {\n                var dns = (CloudflareDNS) App.getOkHttpClient().dns();\n                if (!dns.noProxy) {\n                    prefDoH.setEnabled(false);\n                    prefDoH.setVisible(false);\n                    var group = prefDoH.getParent();\n                    assert group != null;\n                    group.setVisible(false);\n                }\n                prefDoH.setOnPreferenceChangeListener((p, v) -> {\n                    dns.DoH = (boolean) v;\n                    return true;\n                });\n            }\n\n            SimpleMenuPreference language = findPreference(\"language\");\n            if (language != null) {\n                var tag = language.getValue();\n                var userLocale = App.getLocale();\n                var entries = new ArrayList<CharSequence>();\n                var lstLang = LangList.LOCALES;\n                for (var lang : lstLang) {\n                    if (lang.equals(SYSTEM)) {\n                        entries.add(getString(rikka.core.R.string.follow_system));\n                        continue;\n                    }\n                    var locale = Locale.forLanguageTag(lang);\n                    entries.add(HtmlCompat.fromHtml(locale.getDisplayName(locale), HtmlCompat.FROM_HTML_MODE_LEGACY));\n                }\n                language.setEntries(entries.toArray(new CharSequence[0]));\n                language.setEntryValues(lstLang);\n                if (TextUtils.isEmpty(tag) || SYSTEM.equals(tag)) {\n                    language.setSummary(getString(rikka.core.R.string.follow_system));\n                } else {\n                    var locale = Locale.forLanguageTag(tag);\n                    language.setSummary(!TextUtils.isEmpty(locale.getScript()) ? locale.getDisplayScript(userLocale) : locale.getDisplayName(userLocale));\n                }\n                language.setOnPreferenceChangeListener((preference, newValue) -> {\n                    var app = App.getInstance();\n                    var locale = App.getLocale((String) newValue);\n                    var res = app.getResources();\n                    var config = res.getConfiguration();\n                    config.setLocale(locale);\n                    LocaleDelegate.setDefaultLocale(locale);\n                    //noinspection deprecation\n                    res.updateConfiguration(config, res.getDisplayMetrics());\n                    MainActivity activity = (MainActivity) getActivity();\n                    if (activity != null) {\n                        activity.restart();\n                    }\n                    return true;\n                });\n            }\n\n            Preference translation = findPreference(\"translation\");\n            if (translation != null) {\n                translation.setOnPreferenceClickListener(preference -> {\n                    NavUtil.startURL(requireActivity(), \"https://crowdin.com/project/lsposed_jingmatrix\");\n                    return true;\n                });\n                translation.setSummary(getString(R.string.settings_translation_summary, getString(R.string.app_name)));\n            }\n\n            Preference translation_contributors = findPreference(\"translation_contributors\");\n            if (translation_contributors != null) {\n                var translators = HtmlCompat.fromHtml(getString(R.string.translators), HtmlCompat.FROM_HTML_MODE_LEGACY);\n                if (translators.toString().equals(\"null\")) {\n                    translation_contributors.setVisible(false);\n                } else {\n                    translation_contributors.setSummary(translators);\n                }\n            }\n            SimpleMenuPreference channel = findPreference(\"update_channel\");\n            if (channel != null) {\n                channel.setOnPreferenceChangeListener((preference, newValue) -> {\n                    var repoLoader = RepoLoader.getInstance();\n                    repoLoader.updateLatestVersion(String.valueOf(newValue));\n                    return true;\n                });\n            }\n        }\n\n        @NonNull\n        @Override\n        public RecyclerView onCreateRecyclerView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, Bundle savedInstanceState) {\n            BorderRecyclerView recyclerView = (BorderRecyclerView) super.onCreateRecyclerView(inflater, parent, savedInstanceState);\n            RecyclerViewKt.fixEdgeEffect(recyclerView, false, true);\n            recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> parentFragment.binding.appBar.setLifted(!top));\n            var fragment = getParentFragment();\n            if (fragment instanceof SettingsFragment settingsFragment) {\n                View.OnClickListener l = v -> {\n                    settingsFragment.binding.appBar.setExpanded(true, true);\n                    recyclerView.smoothScrollToPosition(0);\n                };\n                settingsFragment.binding.toolbar.setOnClickListener(l);\n                settingsFragment.binding.clickView.setOnClickListener(l);\n            }\n            return recyclerView;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/widget/EmptyStateRecyclerView.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.text.Layout;\nimport android.text.StaticLayout;\nimport android.text.TextPaint;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\n\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.ConcatAdapter;\n\nimport org.lsposed.manager.R;\nimport org.lsposed.manager.util.SimpleStatefulAdaptor;\n\nimport rikka.core.util.ResourceUtils;\n\npublic class EmptyStateRecyclerView extends StatefulRecyclerView {\n    private final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);\n    private final String emptyText;\n\n    public EmptyStateRecyclerView(Context context) {\n        this(context, null);\n    }\n\n    public EmptyStateRecyclerView(Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public EmptyStateRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        DisplayMetrics dm = context.getResources().getDisplayMetrics();\n\n        paint.setColor(ResourceUtils.resolveColor(context.getTheme(), android.R.attr.textColorSecondary));\n        paint.setTextSize(16f * dm.scaledDensity);\n\n        emptyText = context.getString(R.string.list_empty);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        var adapter = getAdapter();\n        if (adapter instanceof ConcatAdapter) {\n            for (var a : ((ConcatAdapter) adapter).getAdapters()) {\n                if (a instanceof EmptyStateAdapter) {\n                    adapter = a;\n                    break;\n                }\n            }\n        }\n        if (adapter instanceof EmptyStateAdapter && ((EmptyStateAdapter<?>) adapter).isLoaded() && adapter.getItemCount() == 0) {\n            final int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\n            final int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();\n\n            var textLayout = new StaticLayout(emptyText, paint, width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);\n\n            canvas.save();\n            canvas.translate(getPaddingLeft(), (height >> 1) + getPaddingTop() - (textLayout.getHeight() >> 1));\n\n            textLayout.draw(canvas);\n\n            canvas.restore();\n        }\n    }\n\n    public abstract static class EmptyStateAdapter<T extends ViewHolder> extends SimpleStatefulAdaptor<T> {\n        abstract public boolean isLoaded();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/widget/ExpandableTextView.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors-->\n */\n\npackage org.lsposed.manager.ui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.text.Layout;\nimport android.text.SpannableString;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.text.TextPaint;\nimport android.text.method.LinkMovementMethod;\nimport android.text.style.ClickableSpan;\nimport android.transition.TransitionManager;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.android.material.textview.MaterialTextView;\n\nimport org.lsposed.manager.R;\n\npublic class ExpandableTextView extends MaterialTextView {\n    private CharSequence text = null;\n    private int nextLines = 0;\n    private final int maxLines;\n    private final SpannableString collapse;\n    private final SpannableString expand;\n    private final SpannableStringBuilder sb = new SpannableStringBuilder();\n    private int lineCount = 0;\n\n    public ExpandableTextView(Context context) {\n        this(context, null);\n    }\n\n    public ExpandableTextView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public ExpandableTextView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        maxLines = getMaxLines();\n        collapse = new SpannableString(context.getString(R.string.collapse));\n        ClickableSpan span = new ClickableSpan() {\n            @Override\n            public void onClick(@NonNull View widget) {\n                TransitionManager.beginDelayedTransition((ViewGroup) getParent());\n                setMaxLines(nextLines);\n                ExpandableTextView.super.setText(text);\n            }\n\n            @Override\n            public void updateDrawState(@NonNull TextPaint ds) {\n                ds.setTypeface(Typeface.DEFAULT_BOLD);\n            }\n        };\n        collapse.setSpan(span, 0, collapse.length(), 0);\n        expand = new SpannableString(context.getString(R.string.expand));\n        expand.setSpan(span, 0, expand.length(), 0);\n        setMovementMethod(LinkMovementMethod.getInstance());\n    }\n\n    @Override\n    public void setText(CharSequence text, BufferType type) {\n        this.text = text;\n        super.setText(text, type);\n    }\n\n    @Override\n    public boolean onPreDraw() {\n        this.getViewTreeObserver().removeOnPreDrawListener(this);\n        if (lineCount == 0) {\n            lineCount = getLayout().getLineCount();\n        }\n        if (lineCount > maxLines) {\n            int hintTextOffsetEnd;\n            if (maxLines == getMaxLines()) {\n                nextLines = lineCount + 1;\n                hintTextOffsetEnd = getLayout().getLineStart(getMaxLines() - 1);\n                setTextWithSpan(text, hintTextOffsetEnd - 1, expand);\n            } else if (nextLines == getMaxLines()) {\n                nextLines = maxLines;\n                hintTextOffsetEnd = getLayout().getLineStart(getMaxLines() - 1);\n                setTextWithSpan(text, hintTextOffsetEnd, collapse);\n            }\n        }\n        return super.onPreDraw();\n    }\n\n    private void setTextWithSpan(CharSequence text, int textOffsetEnd,\n                                 SpannableString sbStr) {\n        sb.clearSpans();\n        sb.clear();\n        sb.append(text, 0, textOffsetEnd);\n        sb.append(\"\\n\");\n        sb.append(sbStr);\n        super.setText(sb, BufferType.NORMAL);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        if (getLayout() != null) {\n            lineCount = getLayout().getLineCount();\n        }\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouchEvent(@NonNull MotionEvent event) {\n        Layout layout = this.getLayout();\n        if (layout != null) {\n            int line = layout.getLineForVertical((int) event.getY());\n            int offset = layout.getOffsetForHorizontal(line, event.getX());\n\n            if (getText() instanceof Spanned) {\n                Spanned spanned = (Spanned) getText();\n\n                ClickableSpan[] links = spanned.getSpans(offset, offset, ClickableSpan.class);\n\n                if (links.length == 0) {\n                    return false;\n                } else {\n                    return super.onTouchEvent(event);\n                }\n            }\n        }\n\n        return false;\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Bundle bundle = new Bundle();\n        bundle.putParcelable(\"superState\", super.onSaveInstanceState());\n        bundle.putInt(\"maxLines\", getMaxLines());\n        return bundle;\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        if (state instanceof Bundle) {\n            Bundle bundle = (Bundle) state;\n            setMaxLines(bundle.getInt(\"maxLines\"));\n            state = bundle.getParcelable(\"superState\");\n        }\n        super.onRestoreInstanceState(state);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/widget/LinkifyTextView.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.text.Layout;\nimport android.text.Spanned;\nimport android.text.style.ClickableSpan;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\nimport androidx.annotation.NonNull;\n\npublic class LinkifyTextView extends androidx.appcompat.widget.AppCompatTextView {\n\n    private ClickableSpan mCurrentSpan;\n\n    public LinkifyTextView(Context context) {\n        super(context);\n    }\n\n    public LinkifyTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public LinkifyTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public ClickableSpan getCurrentSpan() {\n        return mCurrentSpan;\n    }\n\n    public void clearCurrentSpan() {\n        mCurrentSpan = null;\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouchEvent(@NonNull MotionEvent event) {\n        // Let the parent or grandparent of TextView to handles click action.\n        // Otherwise click effect like ripple will not work, and if touch area\n        // do not contain a url, the TextView will still get MotionEvent.\n        // onTouchEven must be called with MotionEvent.ACTION_DOWN for each touch\n        // action on it, so we analyze touched url here.\n        if (event.getAction() == MotionEvent.ACTION_DOWN) {\n            mCurrentSpan = null;\n\n            if (getText() instanceof Spanned) {\n                // Get this code from android.text.method.LinkMovementMethod.\n                // Work fine !\n                int x = (int) event.getX();\n                int y = (int) event.getY();\n\n                x -= getTotalPaddingLeft();\n                y -= getTotalPaddingTop();\n\n                x += getScrollX();\n                y += getScrollY();\n\n                Layout layout = getLayout();\n                if (null != layout) {\n                    int line = layout.getLineForVertical(y);\n                    int off = layout.getOffsetForHorizontal(line, x);\n\n                    ClickableSpan[] spans = ((Spanned) getText()).getSpans(off, off, ClickableSpan.class);\n\n                    if (spans.length > 0) {\n                        mCurrentSpan = spans[0];\n                    }\n                }\n            }\n        }\n\n        return super.onTouchEvent(event);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/widget/ScrollWebView.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewParent;\nimport android.webkit.WebView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport rikka.widget.borderview.BorderRecyclerView;\n\npublic class ScrollWebView extends WebView {\n    public ScrollWebView(@NonNull Context context) {\n        super(context);\n    }\n\n    public ScrollWebView(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public ScrollWebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public ScrollWebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (event.getAction() == MotionEvent.ACTION_DOWN) {\n            var viewParent = findViewParentIfNeeds(this);\n            if (viewParent != null) viewParent.requestDisallowInterceptTouchEvent(true);\n        }\n        return super.onTouchEvent(event);\n    }\n\n    @Override\n    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {\n        if (clampedX) {\n            var viewParent = findViewParentIfNeeds(this);\n            if (viewParent != null) viewParent.requestDisallowInterceptTouchEvent(false);\n        }\n        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);\n    }\n\n    private static ViewParent findViewParentIfNeeds(View v) {\n        var parent = v.getParent();\n        if (parent == null) return null;\n        if (parent instanceof RecyclerView && !(parent instanceof BorderRecyclerView)) {\n            return parent;\n        } else if (parent instanceof View) {\n            return findViewParentIfNeeds((View) parent);\n        } else {\n            return parent;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/ui/widget/StatefulRecyclerView.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.ui.widget;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.viewpager2.adapter.StatefulAdapter;\n\nimport rikka.widget.borderview.BorderRecyclerView;\n\npublic class StatefulRecyclerView extends BorderRecyclerView {\n    public StatefulRecyclerView(@NonNull Context context) {\n        super(context);\n    }\n\n    public StatefulRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public StatefulRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Bundle bundle = new Bundle();\n        bundle.putParcelable(\"superState\", super.onSaveInstanceState());\n        var adapter = getAdapter();\n        if (adapter instanceof StatefulAdapter) {\n            bundle.putParcelable(\"adaptor\", ((StatefulAdapter) adapter).saveState());\n        }\n        return bundle;\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        if (state instanceof Bundle) {\n            Bundle bundle = (Bundle) state;\n            super.onRestoreInstanceState(bundle.getParcelable(\"superState\"));\n            var adapter = getAdapter();\n            if (adapter instanceof StatefulAdapter) {\n                ((StatefulAdapter) adapter).restoreState(bundle.getParcelable(\"adaptor\"));\n            }\n        } else {\n            super.onRestoreInstanceState(state);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/AccessibilityUtils.java",
    "content": "package org.lsposed.manager.util;\n\nimport android.content.ContentResolver;\nimport android.provider.Settings;\n\npublic class AccessibilityUtils {\n  public static boolean isAnimationEnabled(ContentResolver cr) {\n    return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f\n        && Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f\n        && Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/AppIconModelLoader.java",
    "content": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * 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\npackage org.lsposed.manager.util;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.graphics.Bitmap;\nimport android.os.Build;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.Px;\n\nimport com.bumptech.glide.Priority;\nimport com.bumptech.glide.load.DataSource;\nimport com.bumptech.glide.load.Options;\nimport com.bumptech.glide.load.data.DataFetcher;\nimport com.bumptech.glide.load.model.ModelLoader;\nimport com.bumptech.glide.load.model.ModelLoaderFactory;\nimport com.bumptech.glide.load.model.MultiModelLoaderFactory;\nimport com.bumptech.glide.signature.ObjectKey;\n\nimport org.lsposed.manager.App;\n\nimport me.zhanghai.android.appiconloader.AppIconLoader;\n\npublic class AppIconModelLoader implements ModelLoader<PackageInfo, Bitmap> {\n    @NonNull\n    private final AppIconLoader mLoader;\n    @NonNull\n    private final Context mContext;\n\n    private AppIconModelLoader(@Px int iconSize, boolean shrinkNonAdaptiveIcons,\n                               @NonNull Context context) {\n        mLoader = new AppIconLoader(iconSize, shrinkNonAdaptiveIcons, context);\n        mContext = context;\n    }\n\n    @Override\n    public boolean handles(@NonNull PackageInfo model) {\n        return true;\n    }\n\n    @Nullable\n    @Override\n    public LoadData<Bitmap> buildLoadData(@NonNull PackageInfo model, int width, int height,\n                                          @NonNull Options options) {\n        var warpApplicationInfo = new ApplicationInfo(model.applicationInfo);\n        warpApplicationInfo.uid = warpApplicationInfo.uid % App.PER_USER_RANGE;\n        var warpPackageInfo = new PackageInfo();\n        warpPackageInfo.applicationInfo = warpApplicationInfo;\n        warpPackageInfo.versionCode = model.versionCode;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            warpPackageInfo.setLongVersionCode(model.getLongVersionCode());\n        }\n        return new LoadData<>(new ObjectKey(AppIconLoader.getIconKey(warpPackageInfo, mContext)),\n                new Fetcher(mLoader, warpApplicationInfo));\n    }\n\n    private static class Fetcher implements DataFetcher<Bitmap> {\n        @NonNull\n        private final AppIconLoader mLoader;\n        @NonNull\n        private final ApplicationInfo mApplicationInfo;\n\n        public Fetcher(@NonNull AppIconLoader loader, @NonNull ApplicationInfo applicationInfo) {\n            mLoader = loader;\n            mApplicationInfo = applicationInfo;\n        }\n\n        @Override\n        public void loadData(@NonNull Priority priority,\n                             @NonNull DataCallback<? super Bitmap> callback) {\n            try {\n                Bitmap icon = mLoader.loadIcon(mApplicationInfo);\n                callback.onDataReady(icon);\n            } catch (Exception e) {\n                callback.onLoadFailed(e);\n            }\n        }\n\n        @Override\n        public void cleanup() {\n        }\n\n        @Override\n        public void cancel() {\n        }\n\n        @NonNull\n        @Override\n        public Class<Bitmap> getDataClass() {\n            return Bitmap.class;\n        }\n\n        @NonNull\n        @Override\n        public DataSource getDataSource() {\n            return DataSource.LOCAL;\n        }\n    }\n\n    public static class Factory implements ModelLoaderFactory<PackageInfo, Bitmap> {\n        @Px\n        private final int mIconSize;\n        private final boolean mShrinkNonAdaptiveIcons;\n        @NonNull\n        private final Context mContext;\n\n        public Factory(@Px int iconSize, boolean shrinkNonAdaptiveIcons, @NonNull Context context) {\n            mIconSize = iconSize;\n            mShrinkNonAdaptiveIcons = shrinkNonAdaptiveIcons;\n            mContext = context.getApplicationContext();\n        }\n\n        @NonNull\n        @Override\n        public ModelLoader<PackageInfo, Bitmap> build(\n                @NonNull MultiModelLoaderFactory multiFactory) {\n            return new AppIconModelLoader(mIconSize, mShrinkNonAdaptiveIcons, mContext);\n        }\n\n        @Override\n        public void teardown() {\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/AppModule.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.graphics.Bitmap;\n\nimport androidx.annotation.NonNull;\n\nimport com.bumptech.glide.Glide;\nimport com.bumptech.glide.Registry;\nimport com.bumptech.glide.annotation.GlideModule;\nimport com.bumptech.glide.module.AppGlideModule;\n\nimport org.lsposed.manager.R;\n\n@GlideModule\npublic class AppModule extends AppGlideModule {\n    @Override\n    public boolean isManifestParsingEnabled() {\n        return false;\n    }\n\n    @Override\n    public void registerComponents(Context context, @NonNull Glide glide, Registry registry) {\n        int iconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);\n        var factory = new AppIconModelLoader.Factory(iconSize, false, context);\n        registry.prepend(PackageInfo.class, Bitmap.class, factory);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/BackupUtils.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.net.Uri;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.adapters.ScopeAdapter;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.zip.GZIPInputStream;\nimport java.util.zip.GZIPOutputStream;\n\nimport rikka.core.os.FileUtils;\n\npublic class BackupUtils {\n    private static final int VERSION = 2;\n\n    public static void backup(Uri uri) throws JSONException, IOException {\n        backup(uri, null);\n    }\n\n    public static void backup(Uri uri, String packageName) throws IOException, JSONException {\n        JSONObject rootObject = new JSONObject();\n        rootObject.put(\"version\", VERSION);\n        JSONArray modulesArray = new JSONArray();\n        var modules = ModuleUtil.getInstance().getModules();\n        if (modules == null) return;\n        for (ModuleUtil.InstalledModule module : modules.values()) {\n            if (packageName != null && !module.packageName.equals(packageName)) {\n                continue;\n            }\n            JSONObject moduleObject = new JSONObject();\n            moduleObject.put(\"enable\", ModuleUtil.getInstance().isModuleEnabled(module.packageName));\n            moduleObject.put(\"package\", module.packageName);\n            List<ScopeAdapter.ApplicationWithEquals> scope = ConfigManager.getModuleScope(module.packageName);\n            JSONArray scopeArray = new JSONArray();\n            for (ScopeAdapter.ApplicationWithEquals s : scope) {\n                JSONObject app = new JSONObject();\n                app.put(\"package\", s.packageName);\n                app.put(\"userId\", s.userId);\n                scopeArray.put(app);\n            }\n            moduleObject.put(\"scope\", scopeArray);\n            modulesArray.put(moduleObject);\n        }\n        rootObject.put(\"modules\", modulesArray);\n        try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(App.getInstance().getContentResolver().openOutputStream(uri))) {\n            gzipOutputStream.write(rootObject.toString().getBytes());\n        }\n    }\n\n    public static void restore(Uri uri) throws JSONException, IOException {\n        restore(uri, null);\n    }\n\n    public static void restore(Uri uri, String packageName) throws IOException, JSONException {\n        try (GZIPInputStream gzipInputStream = new GZIPInputStream(App.getInstance().getContentResolver().openInputStream(uri), 32)) {\n            StringBuilder string = new StringBuilder();\n            try (var os = new ByteArrayOutputStream()) {\n                FileUtils.copy(gzipInputStream, os);\n                string.append(os);\n            }\n            gzipInputStream.close();\n            JSONObject rootObject = new JSONObject(string.toString());\n            int version = rootObject.getInt(\"version\");\n            if (version == VERSION || version == 1) {\n                JSONArray modules = rootObject.getJSONArray(\"modules\");\n                for (int i = 0; i < modules.length(); i++) {\n                    JSONObject moduleObject = modules.getJSONObject(i);\n                    String name = moduleObject.getString(\"package\");\n                    if (packageName != null && !name.equals(packageName)) {\n                        continue;\n                    }\n                    ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(name);\n                    if (module != null) {\n                        var enabled = moduleObject.getBoolean(\"enable\");\n                        ModuleUtil.getInstance().setModuleEnabled(name, enabled);\n                        if (!enabled) continue;\n                        JSONArray scopeArray = moduleObject.getJSONArray(\"scope\");\n                        HashSet<ScopeAdapter.ApplicationWithEquals> scope = new HashSet<>();\n                        for (int j = 0; j < scopeArray.length(); j++) {\n                            if (version == VERSION) {\n                                JSONObject app = scopeArray.getJSONObject(j);\n                                scope.add(new ScopeAdapter.ApplicationWithEquals(app.getString(\"package\"), app.getInt(\"userId\")));\n                            } else {\n                                scope.add(new ScopeAdapter.ApplicationWithEquals(scopeArray.getString(j), 0));\n                            }\n                        }\n                        ConfigManager.setModuleScope(name, module.legacy, scope);\n                    }\n                }\n            } else {\n                throw new IllegalArgumentException(\"Unknown backup file version\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/CloudflareDNS.java",
    "content": "package org.lsposed.manager.util;\n\nimport android.os.Build;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.manager.App;\n\nimport java.net.InetAddress;\nimport java.net.Proxy;\nimport java.net.ProxySelector;\nimport java.net.UnknownHostException;\nimport java.util.List;\n\nimport okhttp3.ConnectionSpec;\nimport okhttp3.Dns;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.dnsoverhttps.DnsOverHttps;\nimport okhttp3.internal.platform.Platform;\n\npublic final class CloudflareDNS implements Dns {\n\n    private static final HttpUrl url = HttpUrl.get(\"https://cloudflare-dns.com/dns-query\");\n    public boolean DoH = App.getPreferences().getBoolean(\"doh\", false);\n    public boolean noProxy = ProxySelector.getDefault().select(url.uri()).get(0) == Proxy.NO_PROXY;\n    private final Dns cloudflare;\n\n    public CloudflareDNS() {\n        var trustManager = Platform.get().platformTrustManager();\n        var tls = ConnectionSpec.RESTRICTED_TLS;\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {\n            //noinspection deprecation\n            tls = new ConnectionSpec.Builder(tls)\n                    .supportsTlsExtensions(false)\n                    .build();\n        }\n        var builder = new DnsOverHttps.Builder()\n                .resolvePrivateAddresses(true)\n                .url(HttpUrl.get(\"https://cloudflare-dns.com/dns-query\"))\n                .client(new OkHttpClient.Builder()\n                        .cache(App.getOkHttpCache())\n                        .sslSocketFactory(new NoSniFactory(), trustManager)\n                        .connectionSpecs(List.of(tls))\n                        .build());\n        try {\n            builder.bootstrapDnsHosts(List.of(\n                    InetAddress.getByName(\"1.1.1.1\"),\n                    InetAddress.getByName(\"1.0.0.1\"),\n                    InetAddress.getByName(\"2606:4700:4700::1111\"),\n                    InetAddress.getByName(\"2606:4700:4700::1001\")));\n        } catch (UnknownHostException ignored) {\n        }\n        cloudflare = builder.build();\n    }\n\n    @NonNull\n    @Override\n    public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {\n        if (DoH && noProxy) {\n            return cloudflare.lookup(hostname);\n        } else {\n            return SYSTEM.lookup(hostname);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/EmptyAccessibilityDelegate.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.os.Bundle;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.accessibility.AccessibilityEvent;\nimport android.view.accessibility.AccessibilityNodeInfo;\nimport android.view.accessibility.AccessibilityNodeProvider;\n\npublic class EmptyAccessibilityDelegate extends View.AccessibilityDelegate {\n\n    @Override\n    public void sendAccessibilityEvent(View host, int eventType) {\n\n    }\n\n    @Override\n    public boolean performAccessibilityAction(View host, int action, Bundle args) {\n        return true;\n    }\n\n    @Override\n    public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {\n\n    }\n\n    @Override\n    public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {\n        return true;\n    }\n\n    @Override\n    public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {\n\n    }\n\n    @Override\n    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {\n\n    }\n\n    @Override\n    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {\n\n    }\n\n    @Override\n    public void addExtraDataToAccessibilityNodeInfo(View host, AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {\n\n    }\n\n    @Override\n    public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) {\n        return true;\n    }\n\n    @Override\n    public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/ModuleUtil.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.PackageManager.NameNotFoundException;\nimport android.os.Build;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\n\nimport org.lsposed.lspd.models.UserInfo;\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.ConfigManager;\nimport org.lsposed.manager.repo.RepoLoader;\nimport org.lsposed.manager.repo.model.OnlineModule;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipFile;\n\npublic final class ModuleUtil {\n    // xposedminversion below this\n    public static int MIN_MODULE_VERSION = 2; // reject modules with\n    private static ModuleUtil instance = null;\n    private final PackageManager pm;\n    private final Set<ModuleListener> listeners = ConcurrentHashMap.newKeySet();\n    private HashSet<String> enabledModules = new HashSet<>();\n    private List<UserInfo> users = new ArrayList<>();\n    private Map<Pair<String, Integer>, InstalledModule> installedModules = new HashMap<>();\n    private boolean modulesLoaded = false;\n\n    static final int MATCH_ANY_USER = 0x00400000; // PackageManager.MATCH_ANY_USER\n\n    static final int MATCH_ALL_FLAGS = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER;\n\n    private ModuleUtil() {\n        pm = App.getInstance().getPackageManager();\n    }\n\n    public boolean isModulesLoaded() {\n        return modulesLoaded;\n    }\n\n    public static synchronized ModuleUtil getInstance() {\n        if (instance == null) {\n            instance = new ModuleUtil();\n            App.getExecutorService().submit(instance::reloadInstalledModules);\n        }\n        return instance;\n    }\n\n    public static int extractIntPart(String str) {\n        int result = 0, length = str.length();\n        for (int offset = 0; offset < length; offset++) {\n            char c = str.charAt(offset);\n            if ('0' <= c && c <= '9')\n                result = result * 10 + (c - '0');\n            else\n                break;\n        }\n        return result;\n    }\n\n    public static ZipFile getModernModuleApk(ApplicationInfo info) {\n        String[] apks;\n        if (info.splitSourceDirs != null) {\n            apks = Arrays.copyOf(info.splitSourceDirs, info.splitSourceDirs.length + 1);\n            apks[info.splitSourceDirs.length] = info.sourceDir;\n        } else apks = new String[]{info.sourceDir};\n        ZipFile zip = null;\n        for (var apk : apks) {\n            try {\n                zip = new ZipFile(apk);\n                if (zip.getEntry(\"META-INF/xposed/java_init.list\") != null) {\n                    return zip;\n                }\n                zip.close();\n                zip = null;\n            } catch (IOException ignored) {\n            }\n        }\n        return zip;\n    }\n\n    public static boolean isLegacyModule(ApplicationInfo info) {\n        return info.metaData != null && info.metaData.containsKey(\"xposedminversion\");\n    }\n\n    synchronized public void reloadInstalledModules() {\n        modulesLoaded = false;\n        if (!ConfigManager.isBinderAlive()) {\n            modulesLoaded = true;\n            return;\n        }\n\n        Map<Pair<String, Integer>, InstalledModule> modules = new HashMap<>();\n        var users = ConfigManager.getUsers();\n        for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA | MATCH_ALL_FLAGS, false)) {\n            ApplicationInfo app = pkg.applicationInfo;\n\n            var modernApk = getModernModuleApk(app);\n            if (modernApk != null || isLegacyModule(app)) {\n                modules.computeIfAbsent(Pair.create(pkg.packageName, app.uid / App.PER_USER_RANGE), k -> new InstalledModule(pkg, modernApk));\n            }\n        }\n\n        installedModules = modules;\n\n        this.users = users;\n\n        enabledModules = new HashSet<>(Arrays.asList(ConfigManager.getEnabledModules()));\n        modulesLoaded = true;\n        listeners.forEach(ModuleListener::onModulesReloaded);\n    }\n\n    @Nullable\n    public List<UserInfo> getUsers() {\n        return modulesLoaded ? users : null;\n    }\n\n    public InstalledModule reloadSingleModule(String packageName, int userId) {\n        return reloadSingleModule(packageName, userId, false);\n    }\n\n    public InstalledModule reloadSingleModule(String packageName, int userId, boolean packageFullyRemoved) {\n        if (packageFullyRemoved && isModuleEnabled(packageName)) {\n            enabledModules.remove(packageName);\n            listeners.forEach(ModuleListener::onModulesReloaded);\n        }\n        PackageInfo pkg;\n\n        try {\n            pkg = ConfigManager.getPackageInfo(packageName, PackageManager.GET_META_DATA, userId);\n        } catch (NameNotFoundException e) {\n            InstalledModule old = installedModules.remove(Pair.create(packageName, userId));\n            if (old != null) listeners.forEach(i -> i.onSingleModuleReloaded(old));\n            return null;\n        }\n\n        ApplicationInfo app = pkg.applicationInfo;\n        var modernApk = getModernModuleApk(app);\n        if (modernApk != null || isLegacyModule(app)) {\n            InstalledModule module = new InstalledModule(pkg, modernApk);\n            installedModules.put(Pair.create(packageName, userId), module);\n            listeners.forEach(i -> i.onSingleModuleReloaded(module));\n            return module;\n        } else {\n            InstalledModule old = installedModules.remove(Pair.create(packageName, userId));\n            if (old != null) listeners.forEach(i -> i.onSingleModuleReloaded(old));\n            return null;\n        }\n    }\n\n    @Nullable\n    public InstalledModule getModule(String packageName, int userId) {\n        return modulesLoaded ? installedModules.get(Pair.create(packageName, userId)) : null;\n    }\n\n    @Nullable\n    public InstalledModule getModule(String packageName) {\n        return getModule(packageName, 0);\n    }\n\n    @Nullable\n    synchronized public Map<Pair<String, Integer>, InstalledModule> getModules() {\n        return modulesLoaded ? installedModules : null;\n    }\n\n    public boolean setModuleEnabled(String packageName, boolean enabled) {\n        if (!ConfigManager.setModuleEnabled(packageName, enabled)) {\n            return false;\n        }\n        if (enabled) {\n            enabledModules.add(packageName);\n        } else {\n            enabledModules.remove(packageName);\n        }\n        return true;\n    }\n\n    public boolean isModuleEnabled(String packageName) {\n        return enabledModules.contains(packageName);\n    }\n\n    public int getEnabledModulesCount() {\n        return modulesLoaded ? enabledModules.size() : -1;\n    }\n\n    public void addListener(ModuleListener listener) {\n        listeners.add(listener);\n    }\n\n    public void removeListener(ModuleListener listener) {\n        listeners.remove(listener);\n    }\n\n    public interface ModuleListener {\n        /**\n         * Called whenever one (previously or now) installed module has been\n         * reloaded\n         */\n        default void onSingleModuleReloaded(InstalledModule module) {\n\n        }\n\n        default void onModulesReloaded() {\n\n        }\n    }\n\n    public class InstalledModule {\n        //private static final int FLAG_FORWARD_LOCK = 1 << 29;\n        public final int userId;\n        public final String packageName;\n        public final String versionName;\n        public final long versionCode;\n        public final boolean legacy;\n        public final int minVersion;\n        public final int targetVersion;\n        public final boolean staticScope;\n        public final long installTime;\n        public final long updateTime;\n        public final ApplicationInfo app;\n        public final PackageInfo pkg;\n        private String appName; // loaded lazily\n        private String description; // loaded lazily\n        private List<String> scopeList; // loaded lazily\n\n        private InstalledModule(PackageInfo pkg, ZipFile modernModuleApk) {\n            app = pkg.applicationInfo;\n            this.pkg = pkg;\n            userId = pkg.applicationInfo.uid / App.PER_USER_RANGE;\n            packageName = pkg.packageName;\n            versionName = pkg.versionName;\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {\n                versionCode = pkg.versionCode;\n            } else {\n                versionCode = pkg.getLongVersionCode();\n            }\n            installTime = pkg.firstInstallTime;\n            updateTime = pkg.lastUpdateTime;\n            legacy = modernModuleApk == null;\n\n            if (legacy) {\n                Object minVersionRaw = app.metaData.get(\"xposedminversion\");\n                if (minVersionRaw instanceof Integer) {\n                    minVersion = (Integer) minVersionRaw;\n                } else if (minVersionRaw instanceof String) {\n                    minVersion = extractIntPart((String) minVersionRaw);\n                } else {\n                    minVersion = 0;\n                }\n                targetVersion = minVersion; // legacy modules don't have a target version\n                staticScope = false;\n            } else {\n                int minVersion = 100;\n                int targetVersion = 100;\n                boolean staticScope = false;\n                try (modernModuleApk) {\n                    var propEntry = modernModuleApk.getEntry(\"META-INF/xposed/module.prop\");\n                    if (propEntry != null) {\n                        var prop = new Properties();\n                        prop.load(modernModuleApk.getInputStream(propEntry));\n                        minVersion = extractIntPart(prop.getProperty(\"minApiVersion\"));\n                        targetVersion = extractIntPart(prop.getProperty(\"targetApiVersion\"));\n                        staticScope = TextUtils.equals(prop.getProperty(\"staticScope\"), \"true\");\n                    }\n                    var scopeEntry = modernModuleApk.getEntry(\"META-INF/xposed/scope.list\");\n                    if (scopeEntry != null) {\n                        try (var reader = new BufferedReader(new InputStreamReader(modernModuleApk.getInputStream(scopeEntry)))) {\n                            scopeList = reader.lines().collect(Collectors.toList());\n                        }\n                    } else {\n                        scopeList = Collections.emptyList();\n                    }\n                } catch (IOException | OutOfMemoryError e) {\n                    Log.e(App.TAG, \"Error while closing modern module APK\", e);\n                }\n                this.minVersion = minVersion;\n                this.targetVersion = targetVersion;\n                this.staticScope = staticScope;\n            }\n        }\n\n        public boolean isInstalledOnExternalStorage() {\n            return (app.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;\n        }\n\n        public String getAppName() {\n            if (appName == null)\n                appName = app.loadLabel(pm).toString();\n            return appName;\n        }\n\n        public String getDescription() {\n            if (this.description != null) return this.description;\n            String descriptionTmp = \"\";\n            if (legacy) {\n                Object descriptionRaw = app.metaData.get(\"xposeddescription\");\n                if (descriptionRaw instanceof String) {\n                    descriptionTmp = ((String) descriptionRaw).trim();\n                } else if (descriptionRaw instanceof Integer) {\n                    try {\n                        int resId = (Integer) descriptionRaw;\n                        if (resId != 0)\n                            descriptionTmp = pm.getResourcesForApplication(app).getString(resId).trim();\n                    } catch (Exception ignored) {\n                    }\n                }\n            } else {\n                var des = app.loadDescription(pm);\n                if (des != null) descriptionTmp = des.toString();\n            }\n            this.description = descriptionTmp;\n            return this.description;\n        }\n\n        public List<String> getScopeList() {\n            if (scopeList != null) return scopeList;\n            List<String> list = null;\n            try {\n                int scopeListResourceId = app.metaData.getInt(\"xposedscope\");\n                if (scopeListResourceId != 0) {\n                    list = Arrays.asList(pm.getResourcesForApplication(app).getStringArray(scopeListResourceId));\n                } else {\n                    String scopeListString = app.metaData.getString(\"xposedscope\");\n                    if (scopeListString != null)\n                        list = Arrays.asList(scopeListString.split(\";\"));\n                }\n            } catch (Exception ignored) {\n            }\n            if (list == null) {\n                OnlineModule module = RepoLoader.getInstance().getOnlineModule(packageName);\n                if (module != null && module.getScope() != null) {\n                    list = module.getScope();\n                }\n            }\n            if (list != null) {\n                //For historical reasons, legacy modules use the opposite name.\n                //https://github.com/rovo89/XposedBridge/commit/6b49688c929a7768f3113b4c65b429c7a7032afa\n                list.replaceAll(s ->\n                    switch (s) {\n                        case \"android\" -> \"system\";\n                        case \"system\" -> \"android\";\n                        default -> s;\n                    }\n                );\n                scopeList = list;\n            }\n            return scopeList;\n        }\n\n        public PackageInfo getPackageInfo() {\n            return pkg;\n        }\n\n        @NonNull\n        @Override\n        public String toString() {\n            return getAppName();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/NavUtil.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.net.Uri;\nimport android.widget.Toast;\n\nimport androidx.browser.customtabs.CustomTabColorSchemeParams;\nimport androidx.browser.customtabs.CustomTabsIntent;\n\nimport rikka.core.util.ResourceUtils;\n\npublic final class NavUtil {\n\n    public static void startURL(Activity activity, Uri uri) {\n        CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();\n        customTabsIntent.setShowTitle(true);\n        CustomTabColorSchemeParams params = new CustomTabColorSchemeParams.Builder()\n                .setToolbarColor(ResourceUtils.resolveColor(activity.getTheme(), android.R.attr.colorBackground))\n                .setNavigationBarColor(ResourceUtils.resolveColor(activity.getTheme(), android.R.attr.navigationBarColor))\n                .setNavigationBarDividerColor(0)\n                .build();\n        customTabsIntent.setDefaultColorSchemeParams(params);\n        boolean night = ResourceUtils.isNightMode(activity.getResources().getConfiguration());\n        customTabsIntent.setColorScheme(night ? CustomTabsIntent.COLOR_SCHEME_DARK : CustomTabsIntent.COLOR_SCHEME_LIGHT);\n        try {\n            customTabsIntent.build().launchUrl(activity, uri);\n        } catch (ActivityNotFoundException ignored) {\n            Toast.makeText(activity, uri.toString(), Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    public static void startURL(Activity activity, String url) {\n        startURL(activity, Uri.parse(url));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/NoSniFactory.java",
    "content": "package org.lsposed.manager.util;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.Socket;\n\nimport javax.net.ssl.SSLSocketFactory;\n\npublic final class NoSniFactory extends SSLSocketFactory {\n    private static final SSLSocketFactory defaultFactory = (SSLSocketFactory) getDefault();\n    @SuppressWarnings(\"deprecation\")\n    private static final android.net.SSLCertificateSocketFactory openSSLSocket =\n            (android.net.SSLCertificateSocketFactory) android.net.SSLCertificateSocketFactory\n                    .getDefault(1000);\n\n    @Override\n    public String[] getDefaultCipherSuites() {\n        return defaultFactory.getDefaultCipherSuites();\n    }\n\n    @Override\n    public String[] getSupportedCipherSuites() {\n        return defaultFactory.getSupportedCipherSuites();\n    }\n\n    @Override\n    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {\n        return config(defaultFactory.createSocket(s, host, port, autoClose));\n    }\n\n    @Override\n    public Socket createSocket(String host, int port) throws IOException {\n        return config(defaultFactory.createSocket(host, port));\n    }\n\n    @Override\n    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {\n        return config(defaultFactory.createSocket(host, port, localHost, localPort));\n    }\n\n    @Override\n    public Socket createSocket(InetAddress host, int port) throws IOException {\n        return config(defaultFactory.createSocket(host, port));\n    }\n\n    @Override\n    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {\n        return config(defaultFactory.createSocket(address, port, localAddress, localPort));\n    }\n\n    private Socket config(Socket socket) {\n        try {\n            openSSLSocket.setHostname(socket, null);\n            openSSLSocket.setUseSessionTickets(socket, true);\n        } catch (IllegalArgumentException ignored) {\n        }\n        return socket;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/ShortcutUtil.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.PendingIntent;\nimport android.content.BroadcastReceiver;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.IntentSender;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ShortcutInfo;\nimport android.content.pm.ShortcutManager;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.AdaptiveIconDrawable;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.Icon;\nimport android.graphics.drawable.LayerDrawable;\nimport android.os.Build;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class ShortcutUtil {\n    private static final String SHORTCUT_ID = \"org.lsposed.manager.shortcut\";\n\n    private static Bitmap getBitmap(Context context, int id) {\n        var r = context.getResources();\n        var res = r.getDrawable(id, context.getTheme());\n        if (res instanceof BitmapDrawable) {\n            return ((BitmapDrawable) res).getBitmap();\n        } else {\n            if (res instanceof AdaptiveIconDrawable) {\n                var layers = new Drawable[]{((AdaptiveIconDrawable) res).getBackground(),\n                        ((AdaptiveIconDrawable) res).getForeground()};\n                res = new LayerDrawable(layers);\n            }\n            var bitmap = Bitmap.createBitmap(res.getIntrinsicWidth(),\n                    res.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);\n            var canvas = new Canvas(bitmap);\n            res.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n            res.draw(canvas);\n            return bitmap;\n        }\n    }\n\n    private static Intent getLaunchIntent(Context context) {\n        var pm = context.getPackageManager();\n        var pkg = context.getPackageName();\n        var intent = pm.getLaunchIntentForPackage(pkg);\n        if (intent == null) {\n            try {\n                var pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);\n                if (pkgInfo.activities != null) {\n                    for (var activityInfo : pkgInfo.activities) {\n                        if (activityInfo.processName.equals(activityInfo.packageName)) {\n                            intent = new Intent(Intent.ACTION_MAIN);\n                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                            intent.setComponent(new ComponentName(pkg, activityInfo.name));\n                            break;\n                        }\n                    }\n                }\n            } catch (PackageManager.NameNotFoundException ignored) {\n            }\n        }\n        if (intent != null) {\n            var categories = intent.getCategories();\n            if (categories != null) {\n                categories.clear();\n            }\n            intent.addCategory(\"org.lsposed.manager.LAUNCH_MANAGER\");\n            intent.setPackage(pkg);\n        }\n        return intent;\n    }\n\n    @SuppressLint(\"InlinedApi\")\n    private static IntentSender registerReceiver(Context context, Runnable task) {\n        if (task == null) return null;\n        var uuid = UUID.randomUUID().toString();\n        var filter = new IntentFilter(uuid);\n        var permission = \"android.permission.CREATE_USERS\";\n        var receiver = new BroadcastReceiver() {\n            @Override\n            public void onReceive(Context c, Intent intent) {\n                if (!uuid.equals(intent.getAction())) return;\n                context.unregisterReceiver(this);\n                task.run();\n            }\n        };\n        context.registerReceiver(receiver, filter, permission,\n                null/* main thread */, Context.RECEIVER_EXPORTED);\n\n        var intent = new Intent(uuid);\n        int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;\n        return PendingIntent.getBroadcast(context, 0, intent, flags).getIntentSender();\n    }\n\n    private static ShortcutInfo.Builder getShortcutBuilder(Context context) {\n        var builder = new ShortcutInfo.Builder(context, SHORTCUT_ID)\n                .setShortLabel(context.getString(R.string.app_name))\n                .setIntent(getLaunchIntent(context))\n                .setIcon(Icon.createWithAdaptiveBitmap(getBitmap(context,\n                        R.drawable.ic_launcher)));\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            var activity = new ComponentName(context.getPackageName(),\n                    \"android.app.AppDetailsActivity\");\n            builder.setActivity(activity);\n        }\n        return builder;\n    }\n\n    public static boolean isRequestPinShortcutSupported(Context context) throws RuntimeException {\n        var sm = context.getSystemService(ShortcutManager.class);\n        return sm.isRequestPinShortcutSupported();\n    }\n\n    public static boolean requestPinLaunchShortcut(Runnable afterPinned) {\n        if (!App.isParasitic) throw new RuntimeException();\n        var context = App.getInstance();\n        var sm = context.getSystemService(ShortcutManager.class);\n        if (!sm.isRequestPinShortcutSupported()) return false;\n        return sm.requestPinShortcut(getShortcutBuilder(context).build(),\n                registerReceiver(context, afterPinned));\n    }\n\n    public static boolean updateShortcut() {\n        if (!isLaunchShortcutPinned()) return false;\n        var context = App.getInstance();\n        var sm = context.getSystemService(ShortcutManager.class);\n        List<ShortcutInfo> shortcutInfoList = new ArrayList<>();\n        shortcutInfoList.add(getShortcutBuilder(context).build());\n        return sm.updateShortcuts(shortcutInfoList);\n    }\n\n    public static boolean isLaunchShortcutPinned() {\n        var context = App.getInstance();\n        var sm = context.getSystemService(ShortcutManager.class);\n        for (var info : sm.getPinnedShortcuts()) {\n            if (SHORTCUT_ID.equals(info.getId())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/SimpleStatefulAdaptor.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.util.SparseArray;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.viewpager2.adapter.StatefulAdapter;\n\nimport java.util.HashMap;\nimport java.util.List;\n\npublic abstract class SimpleStatefulAdaptor<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> implements StatefulAdapter {\n    HashMap<Long, SparseArray<Parcelable>> states = new HashMap<>();\n    protected RecyclerView rv = null;\n\n    public SimpleStatefulAdaptor() {\n        setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY);\n    }\n\n    @Override\n    @CallSuper\n    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {\n        rv = recyclerView;\n        super.onAttachedToRecyclerView(recyclerView);\n    }\n\n    @Override\n    public void onViewRecycled(@NonNull T holder) {\n        saveStateOf(holder);\n        super.onViewRecycled(holder);\n    }\n\n    @CallSuper\n    @Override\n    public final void onBindViewHolder(@NonNull T holder, int position, @NonNull List<Object> payloads) {\n        var state = states.remove(holder.getItemId());\n        if (state != null) {\n            holder.itemView.restoreHierarchyState(state);\n        }\n        onBindViewHolder(holder, position);\n    }\n\n    private void saveStateOf(@NonNull RecyclerView.ViewHolder holder) {\n        var state = new SparseArray<Parcelable>();\n        holder.itemView.saveHierarchyState(state);\n        states.put(holder.getItemId(), state);\n    }\n\n    @NonNull\n    public Parcelable saveState() {\n        for (int childCount = rv.getChildCount(), i = 0; i < childCount; ++i) {\n            saveStateOf(rv.getChildViewHolder(rv.getChildAt(i)));\n        }\n\n        var out = new Bundle();\n        for (var state : states.entrySet()) {\n            var item = new Bundle();\n            for (int i = 0; i < state.getValue().size(); ++i) {\n                item.putParcelable(String.valueOf(state.getValue().keyAt(i)), state.getValue().valueAt(i));\n            }\n            out.putParcelable(String.valueOf(state.getKey()), item);\n        }\n        return out;\n    }\n\n    @Override\n    public void restoreState(@NonNull Parcelable savedState) {\n        if (savedState instanceof Bundle) {\n            for (var stateKey : ((Bundle) savedState).keySet()) {\n                var array = new SparseArray<Parcelable>();\n                var state = ((Bundle) savedState).getParcelable(stateKey);\n                if (state instanceof Bundle) {\n                    for (var itemKey : ((Bundle) state).keySet()) {\n                        var item = ((Bundle) state).getParcelable(itemKey);\n                        array.put(Integer.parseInt(itemKey), item);\n                    }\n                }\n                states.put(Long.parseLong(stateKey), array);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/ThemeUtil.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\nimport androidx.annotation.StyleRes;\nimport androidx.appcompat.app.AppCompatDelegate;\n\nimport com.google.android.material.color.DynamicColors;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.R;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport rikka.core.util.ResourceUtils;\n\npublic class ThemeUtil {\n    private static final Map<String, Integer> colorThemeMap = new HashMap<>();\n    private static final SharedPreferences preferences;\n\n    public static final String MODE_NIGHT_FOLLOW_SYSTEM = \"MODE_NIGHT_FOLLOW_SYSTEM\";\n    public static final String MODE_NIGHT_NO = \"MODE_NIGHT_NO\";\n    public static final String MODE_NIGHT_YES = \"MODE_NIGHT_YES\";\n\n    static {\n        preferences = App.getPreferences();\n        colorThemeMap.put(\"SAKURA\", R.style.ThemeOverlay_MaterialSakura);\n        colorThemeMap.put(\"MATERIAL_RED\", R.style.ThemeOverlay_MaterialRed);\n        colorThemeMap.put(\"MATERIAL_PINK\", R.style.ThemeOverlay_MaterialPink);\n        colorThemeMap.put(\"MATERIAL_PURPLE\", R.style.ThemeOverlay_MaterialPurple);\n        colorThemeMap.put(\"MATERIAL_DEEP_PURPLE\", R.style.ThemeOverlay_MaterialDeepPurple);\n        colorThemeMap.put(\"MATERIAL_INDIGO\", R.style.ThemeOverlay_MaterialIndigo);\n        colorThemeMap.put(\"MATERIAL_BLUE\", R.style.ThemeOverlay_MaterialBlue);\n        colorThemeMap.put(\"MATERIAL_LIGHT_BLUE\", R.style.ThemeOverlay_MaterialLightBlue);\n        colorThemeMap.put(\"MATERIAL_CYAN\", R.style.ThemeOverlay_MaterialCyan);\n        colorThemeMap.put(\"MATERIAL_TEAL\", R.style.ThemeOverlay_MaterialTeal);\n        colorThemeMap.put(\"MATERIAL_GREEN\", R.style.ThemeOverlay_MaterialGreen);\n        colorThemeMap.put(\"MATERIAL_LIGHT_GREEN\", R.style.ThemeOverlay_MaterialLightGreen);\n        colorThemeMap.put(\"MATERIAL_LIME\", R.style.ThemeOverlay_MaterialLime);\n        colorThemeMap.put(\"MATERIAL_YELLOW\", R.style.ThemeOverlay_MaterialYellow);\n        colorThemeMap.put(\"MATERIAL_AMBER\", R.style.ThemeOverlay_MaterialAmber);\n        colorThemeMap.put(\"MATERIAL_ORANGE\", R.style.ThemeOverlay_MaterialOrange);\n        colorThemeMap.put(\"MATERIAL_DEEP_ORANGE\", R.style.ThemeOverlay_MaterialDeepOrange);\n        colorThemeMap.put(\"MATERIAL_BROWN\", R.style.ThemeOverlay_MaterialBrown);\n        colorThemeMap.put(\"MATERIAL_BLUE_GREY\", R.style.ThemeOverlay_MaterialBlueGrey);\n    }\n\n    private static final String THEME_DEFAULT = \"DEFAULT\";\n    private static final String THEME_BLACK = \"BLACK\";\n\n    private static boolean isBlackNightTheme() {\n        return preferences.getBoolean(\"black_dark_theme\", false);\n    }\n\n    public static boolean isSystemAccent() {\n        return DynamicColors.isDynamicColorAvailable() && preferences.getBoolean(\"follow_system_accent\", true);\n    }\n\n    public static String getNightTheme(Context context) {\n        if (isBlackNightTheme()\n                && ResourceUtils.isNightMode(context.getResources().getConfiguration()))\n            return THEME_BLACK;\n\n        return THEME_DEFAULT;\n    }\n\n    @StyleRes\n    public static int getNightThemeStyleRes(Context context) {\n        switch (getNightTheme(context)) {\n            case THEME_BLACK:\n                return R.style.ThemeOverlay_Black;\n            case THEME_DEFAULT:\n            default:\n                return R.style.ThemeOverlay;\n        }\n    }\n\n    public static String getColorTheme() {\n        if (isSystemAccent()) {\n            return \"SYSTEM\";\n        }\n        return preferences.getString(\"theme_color\", \"COLOR_BLUE\");\n    }\n\n    @StyleRes\n    public static int getColorThemeStyleRes() {\n        Integer theme = colorThemeMap.get(getColorTheme());\n        if (theme == null) {\n            return R.style.ThemeOverlay_MaterialBlue;\n        }\n        return theme;\n    }\n\n    public static int getDarkTheme(String mode) {\n        switch (mode) {\n            case MODE_NIGHT_FOLLOW_SYSTEM:\n            default:\n                return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;\n            case MODE_NIGHT_YES:\n                return AppCompatDelegate.MODE_NIGHT_YES;\n            case MODE_NIGHT_NO:\n                return AppCompatDelegate.MODE_NIGHT_NO;\n        }\n    }\n\n    public static int getDarkTheme() {\n        return getDarkTheme(preferences.getString(\"dark_theme\", MODE_NIGHT_FOLLOW_SYSTEM));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/UpdateUtil.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\nimport org.lsposed.manager.App;\nimport org.lsposed.manager.BuildConfig;\nimport org.lsposed.manager.ConfigManager;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.time.ZoneOffset;\nimport java.util.Locale;\n\nimport okhttp3.Call;\nimport okhttp3.Callback;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okio.Okio;\n\npublic class UpdateUtil {\n    public static void loadRemoteVersion() {\n        var request = new Request.Builder()\n                .url(\"https://api.github.com/repos/JingMatrix/LSPosed/releases/latest\")\n                .addHeader(\"Accept\", \"application/vnd.github.v3+json\")\n                .build();\n        var callback = new Callback() {\n            @Override\n            public void onResponse(@NonNull Call call, @NonNull Response response) {\n                if (!response.isSuccessful()) return;\n                var body = response.body();\n                if (body == null) return;\n                String api = ConfigManager.isBinderAlive() ? ConfigManager.getApi() : \"riru\";\n                try {\n                    var info = JsonParser.parseReader(body.charStream()).getAsJsonObject();\n                    var notes = info.get(\"body\").getAsString();\n                    var assetsArray = info.getAsJsonArray(\"assets\");\n                    for (var assets : assetsArray) {\n                        checkAssets(assets.getAsJsonObject(), notes, api.toLowerCase(Locale.ROOT));\n                    }\n                } catch (Throwable t) {\n                    Log.e(App.TAG, t.getMessage(), t);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull Call call, @NonNull IOException e) {\n                Log.e(App.TAG, \"loadRemoteVersion: \" + e.getMessage());\n                var pref = App.getPreferences();\n                if (pref.getBoolean(\"checked\", false)) return;\n                pref.edit().putBoolean(\"checked\", true).apply();\n            }\n        };\n        App.getOkHttpClient().newCall(request).enqueue(callback);\n    }\n\n    private static void checkAssets(JsonObject assets, String releaseNotes, String api) {\n        var pref = App.getPreferences();\n        var name = assets.get(\"name\").getAsString();\n        var splitName = name.split(\"-\");\n        if (!splitName[3].equals(api)) return;\n        pref.edit()\n                .putInt(\"latest_version\", Integer.parseInt(splitName[2]))\n                .putLong(\"latest_check\", Instant.now().getEpochSecond())\n                .putString(\"release_notes\", releaseNotes)\n                .putString(\"zip_file\", null)\n                .putBoolean(\"checked\", true)\n                .apply();\n        var updatedAt = Instant.parse(assets.get(\"updated_at\").getAsString());\n        var downloadUrl = assets.get(\"browser_download_url\").getAsString();\n        var zipTime = pref.getLong(\"zip_time\", 0);\n        if (!updatedAt.equals(Instant.ofEpochSecond(zipTime))) {\n            var zip = downloadNewZipSync(downloadUrl, name);\n            var size = assets.get(\"size\").getAsLong();\n            if (zip != null && zip.length() == size) {\n                pref.edit()\n                        .putLong(\"zip_time\", updatedAt.getEpochSecond())\n                        .putString(\"zip_file\", zip.getAbsolutePath())\n                        .apply();\n            }\n        }\n    }\n\n    public static boolean needUpdate() {\n        var pref = App.getPreferences();\n        if (!pref.getBoolean(\"checked\", false)) return false;\n        var now = Instant.now();\n        var buildTime = Instant.ofEpochSecond(BuildConfig.BUILD_TIME);\n        var check = pref.getLong(\"latest_check\", 0);\n        if (check > 0) {\n            var checkTime = Instant.ofEpochSecond(check);\n            if (checkTime.atOffset(ZoneOffset.UTC).plusDays(30).toInstant().isBefore(now))\n                return true;\n            var code = pref.getInt(\"latest_version\", 0);\n            return code > BuildConfig.VERSION_CODE;\n        }\n        return buildTime.atOffset(ZoneOffset.UTC).plusDays(30).toInstant().isBefore(now);\n    }\n\n    @Nullable\n    private static File downloadNewZipSync(String url, String name) {\n        var request = new Request.Builder().url(url).build();\n        var zip = new File(App.getInstance().getCacheDir(), name);\n        try (Response response = App.getOkHttpClient().newCall(request).execute()) {\n            var body = response.body();\n            if (!response.isSuccessful() || body == null) return null;\n            try (var source = body.source();\n                 var sink = Okio.buffer(Okio.sink(zip))) {\n                sink.writeAll(source);\n            }\n        } catch (IOException e) {\n            Log.e(App.TAG, \"downloadNewZipSync: \" + e.getMessage());\n            return null;\n        }\n        return zip;\n    }\n\n    public static boolean canInstall() {\n        if (!ConfigManager.isBinderAlive()) return false;\n        var pref = App.getPreferences();\n        var zip = pref.getString(\"zip_file\", null);\n        return zip != null && new File(zip).isFile();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/chrome/CustomTabsURLSpan.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util.chrome;\n\nimport android.app.Activity;\nimport android.text.style.URLSpan;\nimport android.view.View;\n\nimport org.lsposed.manager.util.NavUtil;\n\npublic class CustomTabsURLSpan extends URLSpan {\n\n    private final Activity activity;\n\n    public CustomTabsURLSpan(Activity activity, String url) {\n        super(url);\n        this.activity = activity;\n    }\n\n    @Override\n    public void onClick(View widget) {\n        String url = getURL();\n        NavUtil.startURL(activity, url);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/lsposed/manager/util/chrome/LinkTransformationMethod.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.manager.util.chrome;\n\nimport android.app.Activity;\nimport android.graphics.Rect;\nimport android.text.Spannable;\nimport android.text.Spanned;\nimport android.text.method.TransformationMethod;\nimport android.text.style.URLSpan;\nimport android.view.View;\nimport android.widget.TextView;\n\npublic class LinkTransformationMethod implements TransformationMethod {\n\n    private final Activity activity;\n\n    public LinkTransformationMethod(Activity activity) {\n        this.activity = activity;\n    }\n\n    @Override\n    public CharSequence getTransformation(CharSequence source, View view) {\n        if (view instanceof TextView) {\n            TextView textView = (TextView) view;\n            if (textView.getText() == null || !(textView.getText() instanceof Spannable)) {\n                return source;\n            }\n            Spannable text = (Spannable) textView.getText();\n            URLSpan[] spans = text.getSpans(0, textView.length(), URLSpan.class);\n            for (int i = spans.length - 1; i >= 0; i--) {\n                URLSpan oldSpan = spans[i];\n                int start = text.getSpanStart(oldSpan);\n                int end = text.getSpanEnd(oldSpan);\n                String url = oldSpan.getURL();\n                text.removeSpan(oldSpan);\n                text.setSpan(new CustomTabsURLSpan(activity, url), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n            }\n            return text;\n        }\n        return source;\n    }\n\n    @Override\n    public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Rect previouslyFocusedRect) {\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/anim/fragment_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <alpha\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromAlpha=\"0\"\n        android:toAlpha=\"1\" />\n    <scale\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromXScale=\"0.9\"\n        android:fromYScale=\"0.9\"\n        android:pivotX=\"50%p\"\n        android:pivotY=\"50%p\"\n        android:toXScale=\"1\"\n        android:toYScale=\"1\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/fragment_enter_pop.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <alpha\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromAlpha=\"0\"\n        android:toAlpha=\"1\" />\n    <scale\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromXScale=\"1.1\"\n        android:fromYScale=\"1.1\"\n        android:pivotX=\"50%p\"\n        android:pivotY=\"50%p\"\n        android:toXScale=\"1\"\n        android:toYScale=\"1\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/fragment_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <alpha\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromAlpha=\"1\"\n        android:toAlpha=\"0\" />\n    <scale\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromXScale=\"1\"\n        android:fromYScale=\"1\"\n        android:pivotX=\"50%p\"\n        android:pivotY=\"50%p\"\n        android:toXScale=\"1.1\"\n        android:toYScale=\"1.1\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/fragment_exit_pop.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <alpha\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromAlpha=\"1\"\n        android:toAlpha=\"0\" />\n    <scale\n        android:duration=\"@android:integer/config_shortAnimTime\"\n        android:fromXScale=\"1\"\n        android:fromYScale=\"1\"\n        android:pivotX=\"50%p\"\n        android:pivotY=\"50%p\"\n        android:toXScale=\"0.9\"\n        android:toYScale=\"0.9\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_assignment_checkable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_baseline_assignment_24\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/ic_outline_assignment_24\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_attach_file.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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=\"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": "app/src/main/res/drawable/ic_baseline_add_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_arrow_back_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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    android:autoMirrored=\"true\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_assignment_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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    android:autoMirrored=\"true\">\n  <path\n      android:fillColor=\"@color/ic_launcher_foreground\"\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": "app/src/main/res/drawable/ic_baseline_chat_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_extension_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"@color/ic_launcher_foreground\"\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": "app/src/main/res/drawable/ic_baseline_get_app_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"@color/ic_launcher_foreground\"\n      android:pathData=\"M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_home_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_info_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"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": "app/src/main/res/drawable/ic_baseline_search_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"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": "app/src/main/res/drawable/ic_baseline_settings_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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.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": "app/src/main/res/drawable/ic_baseline_settings_backup_restore_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_extension_checkable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_baseline_extension_24\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/ic_outline_extension_24\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_get_app_checkable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_baseline_get_app_24\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/ic_outline_get_app_24\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_home_checkable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_baseline_home_24\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/ic_outline_home_24\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_keyboard_arrow_down.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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=\"M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <group\n        android:scaleX=\"2\"\n        android:scaleY=\"2\"\n        android:translateX=\"30\"\n        android:translateY=\"30\">\n        <path\n            android:fillColor=\"@color/ic_launcher_foreground\"\n            android:pathData=\"M17.73,12.02l3.98,-3.98c0.39,-0.39 0.39,-1.02 0,-1.41l-4.34,-4.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.98,3.98L8,2.29C7.8,2.1 7.55,2 7.29,2c-0.25,0 -0.51,0.1 -0.7,0.29L2.25,6.63c-0.39,0.39 -0.39,1.02 0,1.41l3.98,3.98L2.25,16c-0.39,0.39 -0.39,1.02 0,1.41l4.34,4.34c0.39,0.39 1.02,0.39 1.41,0l3.98,-3.98 3.98,3.98c0.2,0.2 0.45,0.29 0.71,0.29 0.26,0 0.51,-0.1 0.71,-0.29l4.34,-4.34c0.39,-0.39 0.39,-1.02 0,-1.41l-3.99,-3.98zM12,9c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM7.29,10.96L3.66,7.34l3.63,-3.63 3.62,3.62 -3.62,3.63zM10,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM12,15c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM14,11c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM16.66,20.34l-3.63,-3.62 3.63,-3.63 3.62,3.62 -3.62,3.63z\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape android:shape=\"oval\">\n            <solid android:color=\"@color/ic_launcher_background\" />\n            <size\n                android:width=\"32dp\"\n                android:height=\"32dp\" />\n        </shape>\n    </item>\n    <item\n        android:width=\"32dp\"\n        android:height=\"32dp\">\n        <inset\n            android:inset=\"-8dp\"\n            android:drawable=\"@drawable/ic_launcher_foreground\" />\n    </item>\n</layer-list>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_open_in_browser.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2L5,18L5,8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.89,-2 -2,-2zM12,10l-4,4h3v6h2v-6h3l-4,-4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_android_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M17.6,9.48l1.84,-3.18c0.16,-0.31 0.04,-0.69 -0.26,-0.85c-0.29,-0.15 -0.65,-0.06 -0.83,0.22l-1.88,3.24c-2.86,-1.21 -6.08,-1.21 -8.94,0L5.65,5.67c-0.19,-0.29 -0.58,-0.38 -0.87,-0.2C4.5,5.65 4.41,6.01 4.56,6.3L6.4,9.48C3.3,11.25 1.28,14.44 1,18h22C22.72,14.44 20.7,11.25 17.6,9.48zM7,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25S8.25,13.31 8.25,14C8.25,14.69 7.69,15.25 7,15.25zM17,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25s1.25,0.56 1.25,1.25C18.25,14.69 17.69,15.25 17,15.25z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_app_shortcut_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M17,18H7V6h10v1h2V3c0,-1.1 -0.9,-2 -2,-2H7C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2V18zM7,3h10v1H7V3zM17,21H7v-1h10V21z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20.38,9.62l0.62,1.38l0.62,-1.38l1.38,-0.62l-1.38,-0.62l-0.62,-1.38l-0.62,1.38l-1.38,0.62z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M16,8l-1.25,2.75l-2.75,1.25l2.75,1.25l1.25,2.75l1.25,-2.75l2.75,-1.25l-2.75,-1.25z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M21,13l-0.62,1.38l-1.38,0.62l1.38,0.62l0.62,1.38l0.62,-1.38l1.38,-0.62l-1.38,-0.62z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_assignment_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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    android:autoMirrored=\"true\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M7,15h7v2L7,17zM7,11h10v2L7,13zM7,7h10v2L7,9zM19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-0.14,0 -0.27,0.01 -0.4,0.04 -0.39,0.08 -0.74,0.28 -1.01,0.55 -0.18,0.18 -0.33,0.4 -0.43,0.64 -0.1,0.23 -0.16,0.49 -0.16,0.77v14c0,0.27 0.06,0.54 0.16,0.78s0.25,0.45 0.43,0.64c0.27,0.27 0.62,0.47 1.01,0.55 0.13,0.02 0.26,0.03 0.4,0.03h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,2.75c0.41,0 0.75,0.34 0.75,0.75s-0.34,0.75 -0.75,0.75 -0.75,-0.34 -0.75,-0.75 0.34,-0.75 0.75,-0.75zM19,19L5,19L5,5h14v14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_dark_mode_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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=\"M9.37,5.51C9.19,6.15 9.1,6.82 9.1,7.5c0,4.08 3.32,7.4 7.4,7.4c0.68,0 1.35,-0.09 1.99,-0.27C17.45,17.19 14.93,19 12,19c-3.86,0 -7,-3.14 -7,-7C5,9.07 6.81,6.55 9.37,5.51zM12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9c0,-0.46 -0.04,-0.92 -0.1,-1.36c-0.98,1.37 -2.58,2.26 -4.4,2.26c-2.98,0 -5.4,-2.42 -5.4,-5.4c0,-1.81 0.89,-3.42 2.26,-4.4C12.92,3.04 12.46,3 12,3L12,3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_dns_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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,15v4H5v-4h14m1,-2H4c-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,18.5c-0.82,0 -1.5,-0.67 -1.5,-1.5s0.68,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM19,5v4H5V5h14m1,-2H4c-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,8.5c-0.82,0 -1.5,-0.67 -1.5,-1.5S6.18,5.5 7,5.5s1.5,0.68 1.5,1.5S7.83,8.5 7,8.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_extension_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M10.5,4.5c0.28,0 0.5,0.22 0.5,0.5v2h6v6h2c0.28,0 0.5,0.22 0.5,0.5s-0.22,0.5 -0.5,0.5h-2v6h-2.12c-0.68,-1.75 -2.39,-3 -4.38,-3s-3.7,1.25 -4.38,3H4v-2.12c1.75,-0.68 3,-2.39 3,-4.38 0,-1.99 -1.24,-3.7 -2.99,-4.38L4,7h6V5c0,-0.28 0.22,-0.5 0.5,-0.5m0,-2C9.12,2.5 8,3.62 8,5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8h0.29c1.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-0.3c0,-1.49 1.21,-2.7 2.7,-2.7s2.7,1.21 2.7,2.7v0.3H17c1.1,0 2,-0.9 2,-2v-4c1.38,0 2.5,-1.12 2.5,-2.5S20.38,11 19,11V7c0,-1.1 -0.9,-2 -2,-2h-4c0,-1.38 -1.12,-2.5 -2.5,-2.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_format_color_fill_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M16.56,8.94L7.62,0L6.21,1.41l2.38,2.38L3.44,8.94c-0.59,0.59 -0.59,1.54 0,2.12l5.5,5.5C9.23,16.85 9.62,17 10,17s0.77,-0.15 1.06,-0.44l5.5,-5.5C17.15,10.48 17.15,9.53 16.56,8.94zM5.21,10L10,5.21L14.79,10H5.21zM19,11.5c0,0 -2,2.17 -2,3.5c0,1.1 0.9,2 2,2s2,-0.9 2,-2C21,13.67 19,11.5 19,11.5zM2,20h20v4H2V20z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_get_app_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M13,5v6h1.17L12,13.17 9.83,11L11,11L11,5h2m2,-2L9,3v6L5,9l7,7 7,-7h-4L15,3zM19,18L5,18v2h14v-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_groups_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M4,13c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2C2,12.1 2.9,13 4,13zM5.13,14.1C4.76,14.04 4.39,14 4,14c-0.99,0 -1.93,0.21 -2.78,0.58C0.48,14.9 0,15.62 0,16.43V18l4.5,0v-1.61C4.5,15.56 4.73,14.78 5.13,14.1zM20,13c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2C18,12.1 18.9,13 20,13zM24,16.43c0,-0.81 -0.48,-1.53 -1.22,-1.85C21.93,14.21 20.99,14 20,14c-0.39,0 -0.76,0.04 -1.13,0.1c0.4,0.68 0.63,1.46 0.63,2.29V18l4.5,0V16.43zM16.24,13.65c-1.17,-0.52 -2.61,-0.9 -4.24,-0.9c-1.63,0 -3.07,0.39 -4.24,0.9C6.68,14.13 6,15.21 6,16.39V18h12v-1.61C18,15.21 17.32,14.13 16.24,13.65zM8.07,16c0.09,-0.23 0.13,-0.39 0.91,-0.69c0.97,-0.38 1.99,-0.56 3.02,-0.56s2.05,0.18 3.02,0.56c0.77,0.3 0.81,0.46 0.91,0.69H8.07zM12,8c0.55,0 1,0.45 1,1s-0.45,1 -1,1s-1,-0.45 -1,-1S11.45,8 12,8M12,6c-1.66,0 -3,1.34 -3,3c0,1.66 1.34,3 3,3s3,-1.34 3,-3C15,7.34 13.66,6 12,6L12,6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_home_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M12,5.69l5,4.5V18h-2v-6H9v6H7v-7.81l5,-4.5M12,3L2,12h3v8h6v-6h2v6h6v-8h3L12,3z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_invert_colors_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M17.66,7.93L12,2.27 6.34,7.93c-3.12,3.12 -3.12,8.19 0,11.31C7.9,20.8 9.95,21.58 12,21.58s4.1,-0.78 5.66,-2.34c3.12,-3.12 3.12,-8.19 0,-11.31zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_language_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2s0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2s0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2s-0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2s-0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_merge_type_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_palette_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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=\"M12,22C6.49,22 2,17.51 2,12S6.49,2 12,2s10,4.04 10,9c0,3.31 -2.69,6 -6,6h-1.77c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.12 0.05,0.23 0.13,0.33 0.41,0.47 0.64,1.06 0.64,1.67 0,1.38 -1.12,2.5 -2.5,2.5zM12,4c-4.41,0 -8,3.59 -8,8s3.59,8 8,8c0.28,0 0.5,-0.22 0.5,-0.5 0,-0.16 -0.08,-0.28 -0.14,-0.35 -0.41,-0.46 -0.63,-1.05 -0.63,-1.65 0,-1.38 1.12,-2.5 2.5,-2.5L16,15c2.21,0 4,-1.79 4,-4 0,-3.86 -3.59,-7 -8,-7z\" />\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M6.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\" />\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\" />\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M14.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\" />\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_restore_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M13,3c-4.97,0 -9,4.03 -9,9L1,12l4,3.99L9,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.25,2.52 0.77,-1.28 -3.52,-2.09L13.5,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_settings_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98 0,-0.34 -0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.09,-0.16 -0.26,-0.25 -0.44,-0.25 -0.06,0 -0.12,0.01 -0.17,0.03l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.06,-0.02 -0.12,-0.03 -0.18,-0.03 -0.17,0 -0.34,0.09 -0.43,0.25l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98 0,0.33 0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.09,0.16 0.26,0.25 0.44,0.25 0.06,0 0.12,-0.01 0.17,-0.03l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.06,0.02 0.12,0.03 0.18,0.03 0.17,0 0.34,-0.09 0.43,-0.25l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM17.45,11.27c0.04,0.31 0.05,0.52 0.05,0.73 0,0.21 -0.02,0.43 -0.05,0.73l-0.14,1.13 0.89,0.7 1.08,0.84 -0.7,1.21 -1.27,-0.51 -1.04,-0.42 -0.9,0.68c-0.43,0.32 -0.84,0.56 -1.25,0.73l-1.06,0.43 -0.16,1.13 -0.2,1.35h-1.4l-0.19,-1.35 -0.16,-1.13 -1.06,-0.43c-0.43,-0.18 -0.83,-0.41 -1.23,-0.71l-0.91,-0.7 -1.06,0.43 -1.27,0.51 -0.7,-1.21 1.08,-0.84 0.89,-0.7 -0.14,-1.13c-0.03,-0.31 -0.05,-0.54 -0.05,-0.74s0.02,-0.43 0.05,-0.73l0.14,-1.13 -0.89,-0.7 -1.08,-0.84 0.7,-1.21 1.27,0.51 1.04,0.42 0.9,-0.68c0.43,-0.32 0.84,-0.56 1.25,-0.73l1.06,-0.43 0.16,-1.13 0.2,-1.35h1.39l0.19,1.35 0.16,1.13 1.06,0.43c0.43,0.18 0.83,0.41 1.23,0.71l0.91,0.7 1.06,-0.43 1.27,-0.51 0.7,1.21 -1.07,0.85 -0.89,0.7 0.14,1.13zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-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": "app/src/main/res/drawable/ic_outline_shield_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91c4.59-1.15 8-5.86 8-10.91V5l-8-3zm6 9.09c0 4-2.55 7.7-6 8.83c-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25l6 2.25v4.7z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_speaker_notes_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:autoMirrored=\"true\"\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,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,16L5.17,16l-0.59,0.59 -0.58,0.58L4,4h16v12zM6,12h2v2L6,14zM6,9h2v2L6,11zM6,6h2v2L6,8zM10,12h5v2h-5zM10,9h8v2h-8zM10,6h8v2h-8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_translate_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_bug_report_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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,8h-1.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96l0.93,-0.93c0.39,-0.39 0.39,-1.02 0,-1.41 -0.39,-0.39 -1.02,-0.39 -1.41,0l-1.47,1.47C12.96,5.06 12.49,5 12,5s-0.96,0.06 -1.41,0.17L9.11,3.7c-0.39,-0.39 -1.02,-0.39 -1.41,0 -0.39,0.39 -0.39,1.02 0,1.41l0.92,0.93C7.88,6.55 7.26,7.22 6.81,8L5,8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L5,12c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1v1c0,0.34 0.04,0.67 0.09,1L5,16c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L19,18c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-1.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h1c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-1v-1c0,-0.34 -0.04,-0.67 -0.09,-1L19,10c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1zM13,16h-2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h2c0.55,0 1,0.45 1,1s-0.45,1 -1,1zM13,12h-2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h2c0.55,0 1,0.45 1,1s-0.45,1 -1,1z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_check_circle_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM9.29,16.29L5.7,12.7c-0.39,-0.39 -0.39,-1.02 0,-1.41 0.39,-0.39 1.02,-0.39 1.41,0L10,14.17l6.88,-6.88c0.39,-0.39 1.02,-0.39 1.41,0 0.39,0.39 0.39,1.02 0,1.41l-7.59,7.59c-0.38,0.39 -1.02,0.39 -1.41,0z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_error_outline_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M12,7c0.55,0 1,0.45 1,1v4c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1L11,8c0,-0.55 0.45,-1 1,-1zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM13,17h-2v-2h2v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_settings_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_update_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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=\"M11,8.75v3.68c0,0.35 0.19,0.68 0.49,0.86l3.12,1.85c0.36,0.21 0.82,0.09 1.03,-0.26c0.21,-0.36 0.1,-0.82 -0.26,-1.03l-2.87,-1.71v-3.4C12.5,8.34 12.16,8 11.75,8S11,8.34 11,8.75zM21,9.5V4.21c0,-0.45 -0.54,-0.67 -0.85,-0.35l-1.78,1.78c-1.81,-1.81 -4.39,-2.85 -7.21,-2.6c-4.19,0.38 -7.64,3.75 -8.1,7.94C2.46,16.4 6.69,21 12,21c4.59,0 8.38,-3.44 8.93,-7.88c0.07,-0.6 -0.4,-1.12 -1,-1.12c-0.5,0 -0.92,0.37 -0.98,0.86c-0.43,3.49 -3.44,6.19 -7.05,6.14c-3.71,-0.05 -6.84,-3.18 -6.9,-6.9C4.94,8.2 8.11,5 12,5c1.93,0 3.68,0.79 4.95,2.05l-2.09,2.09C14.54,9.46 14.76,10 15.21,10h5.29C20.78,10 21,9.78 21,9.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_warning_24.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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=\"M4.47,21h15.06c1.54,0 2.5,-1.67 1.73,-3L13.73,4.99c-0.77,-1.33 -2.69,-1.33 -3.46,0L2.74,18c-0.77,1.33 0.19,3 1.73,3zM12,14c-0.55,0 -1,-0.45 -1,-1v-2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v2c0,0.55 -0.45,1 -1,1zM13,18h-2v-2h2v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_save.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<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,-4zM19,19L5,19L5,5h11.17L19,7.83L19,19zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM6,6h9v4L6,10z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_settings_checkable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_baseline_settings_24\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/ic_outline_settings_24\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/shortcut_ic_logs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\" />\n    <foreground>\n        <!-- 44dp icon scaled to 52dp in 72dp, padding = (1-52/44*24/72)/2 -->\n        <inset\n            android:drawable=\"@drawable/ic_baseline_assignment_24\"\n            android:inset=\"30.303%\" />\n    </foreground>\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/drawable/shortcut_ic_modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\" />\n    <foreground>\n        <!-- 44dp icon scaled to 52dp in 72dp, padding = (1-52/44*24/72)/2 -->\n        <inset\n            android:drawable=\"@drawable/ic_baseline_extension_24\"\n            android:inset=\"30.303%\" />\n    </foreground>\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/drawable/shortcut_ic_repo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\" />\n    <foreground>\n        <!-- 44dp icon scaled to 52dp in 72dp, padding = (1-52/44*24/72)/2 -->\n        <inset\n            android:drawable=\"@drawable/ic_baseline_get_app_24\"\n            android:inset=\"30.303%\" />\n    </foreground>\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/drawable/shortcut_ic_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\" />\n    <foreground>\n        <!-- 44dp icon scaled to 52dp in 72dp, padding = (1-52/44*24/72)/2 -->\n        <inset\n            android:drawable=\"@drawable/ic_baseline_settings_24\"\n            android:inset=\"30.303%\" />\n    </foreground>\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/drawable/simple_menu_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape>\n            <solid android:color=\"?attr/colorSurface\" />\n            <corners android:radius=\"?popupBackgroundRadius\" />\n        </shape>\n    </item>\n    <item>\n        <shape>\n            <solid android:color=\"@color/m3_popupmenu_overlay_color\" />\n            <corners android:radius=\"?popupBackgroundRadius\" />\n        </shape>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n      ~ This file is part of LSPosed.\n      ~\n      ~ LSPosed 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      ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n      ~\n      ~ Copyright (C) 2021 LSPosed Contributors\n      -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:edgeToEdge=\"true\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/nav_host_fragment\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:background=\"?android:colorBackground\"\n            app:defaultNavHost=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/nav\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:navGraph=\"@navigation/main_nav\" />\n\n        <com.google.android.material.bottomnavigation.BottomNavigationView\n            android:id=\"@+id/nav\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:fitsSystemWindowsInsets=\"bottom\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:menu=\"@menu/navigation_menu\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:orientation=\"horizontal\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"24dp\">\n\n    <ImageView\n        android:id=\"@+id/design_about_icon\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"48dp\"\n        app:srcCompat=\"@drawable/ic_launcher\" />\n\n    <LinearLayout\n        android:orientation=\"vertical\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\">\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/design_about_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textSize=\"18sp\"\n            android:textAppearance=\"@android:style/TextAppearance.Material.Body1\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/design_about_version\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textSize=\"14sp\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/design_about_info\"\n            android:layout_marginTop=\"24dp\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textSize=\"14sp\"\n            android:textColor=\"?android:textColorSecondary\" />\n\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_item.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<com.google.android.material.textview.MaterialTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/item\"\n    style=\"@style/TextAppearance.MaterialComponents.Body2\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical|start\"\n    android:minHeight=\"?attr/listPreferredItemHeight\"\n    android:paddingStart=\"?attr/listPreferredItemPaddingStart\"\n    android:paddingEnd=\"?attr/listPreferredItemPaddingEnd\"\n    android:textAlignment=\"viewStart\" />\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_title.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<com.google.android.material.textview.MaterialTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/title\"\n    style=\"@style/MaterialAlertDialog.Material3.Title.Text.CenterStacked\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"?attr/dialogPreferredPadding\"\n    android:paddingBottom=\"?attr/dialogPreferredPadding\" />\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_app_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/fragment_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:consumeSystemWindowsInsets=\"@integer/internal_fragment_insets\"\n    app:edgeToEdge=\"true\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_insets\">\n\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"false\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_top_insets\">\n\n        <com.google.android.material.appbar.SubtitleCollapsingToolbarLayout\n            android:id=\"@+id/toolbar_layout\"\n            style=\"?attr/collapsingToolbarLayoutLargeStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/collapsingToolbarLayoutLargeSize\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleCollapseMode=\"scale\">\n\n            <View\n                android:id=\"@+id/click_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:elevation=\"0dp\"\n                android:theme=\"@style/ThemeOverlay.MaterialComponents.ActionBar\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <com.google.android.material.floatingactionbutton.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end|bottom\"\n        android:layout_margin=\"?attr/dialogPreferredPadding\"\n        android:contentDescription=\"@string/module_settings\"\n        android:src=\"@drawable/ic_round_settings_24\"\n        android:tooltipText=\"@string/module_settings\"\n        android:visibility=\"gone\"\n        app:backgroundTint=\"?attr/colorPrimary\"\n        app:layout_behavior=\"@string/hide_bottom_view_on_scroll_behavior\"\n        app:layout_fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\"\n        app:tint=\"?attr/colorOnPrimary\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n            android:id=\"@+id/swipe_refresh_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <org.lsposed.manager.ui.widget.EmptyStateRecyclerView\n                android:id=\"@+id/recycler_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:clipToPadding=\"false\"\n                android:fadeScrollbars=\"true\"\n                android:scrollbarStyle=\"insideOverlay\"\n                android:scrollbars=\"vertical\"\n                app:borderBottomVisibility=\"never\"\n                app:borderTopDrawable=\"@null\"\n                app:borderTopVisibility=\"whenTop\"\n                app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\" />\n        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n    </LinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_compile_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:baselineAligned=\"false\"\n    android:orientation=\"horizontal\"\n    android:paddingStart=\"?attr/dialogPreferredPadding\"\n    android:paddingTop=\"18dp\"\n    android:paddingEnd=\"?attr/dialogPreferredPadding\"\n    android:paddingBottom=\"18dp\">\n\n    <ProgressBar\n        android:id=\"@+id/progress\"\n        style=\"?android:attr/progressBarStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"?attr/dialogPreferredPadding\"\n        android:max=\"10000\" />\n\n    <com.google.android.material.textview.MaterialTextView\n        android:id=\"@+id/message\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"@string/compile_speed_msg\" />\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_home.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/fragment_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:consumeSystemWindowsInsets=\"@integer/internal_fragment_insets\"\n    app:edgeToEdge=\"true\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_insets\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"false\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_top_insets\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/toolbar_layout\"\n            style=\"?collapsingToolbarLayoutLargeStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?collapsingToolbarLayoutLargeSize\"\n            android:fitsSystemWindows=\"false\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleCollapseMode=\"scale\">\n\n            <View\n                android:id=\"@+id/click_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?actionBarSize\"\n                android:elevation=\"0dp\"\n                app:layout_collapseMode=\"pin\"\n                app:logo=\"@drawable/ic_launcher_round\"\n                app:titleMarginStart=\"@dimen/m3_alert_dialog_icon_margin\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n        tools:ignore=\"MissingPrefix\">\n\n        <rikka.widget.borderview.BorderNestedScrollView\n            android:id=\"@+id/nestedScrollView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:scrollbars=\"none\"\n            app:borderBottomVisibility=\"never\"\n            app:borderTopDrawable=\"@null\"\n            app:borderTopVisibility=\"whenTop\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:animateLayoutChanges=\"true\"\n                android:clipChildren=\"false\"\n                android:clipToPadding=\"false\"\n                android:orientation=\"vertical\"\n                android:paddingHorizontal=\"@dimen/m3_appbar_expanded_title_margin_horizontal\"\n                android:paddingVertical=\"?dialogPreferredPadding\"\n                tools:context=\".ui.activity.MainActivity\">\n\n                <com.google.android.material.card.MaterialCardView\n                    android:id=\"@+id/status\"\n                    style=\"@style/Widget.Material3.CardView.Elevated\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:outlineAmbientShadowColor=\"?colorPrimary\"\n                    android:outlineSpotShadowColor=\"?colorPrimary\"\n                    android:stateListAnimator=\"@null\"\n                    app:cardBackgroundColor=\"?colorPrimary\"\n                    app:cardElevation=\"@dimen/home_primary_elevation\"\n                    app:cardPreventCornerOverlap=\"false\"\n                    tools:targetApi=\"p\">\n\n                    <RelativeLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:padding=\"?dialogPreferredPadding\">\n\n                        <ImageView\n                            android:id=\"@+id/status_icon\"\n                            android:layout_width=\"24dp\"\n                            android:layout_height=\"24dp\"\n                            android:layout_centerVertical=\"true\"\n                            android:tint=\"?colorOnPrimary\"\n                            app:srcCompat=\"@drawable/ic_round_check_circle_24\"\n                            tools:ignore=\"ContentDescription\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/status_title\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_marginStart=\"24dp\"\n                            android:layout_toEndOf=\"@id/status_icon\"\n                            android:fontFamily=\"sans-serif-medium\"\n                            android:textAppearance=\"?textAppearanceTitleMedium\"\n                            android:textColor=\"?colorOnPrimary\"\n                            android:textSize=\"16sp\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/status_summary\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_below=\"@id/status_title\"\n                            android:layout_alignStart=\"@id/status_title\"\n                            android:textAppearance=\"?textAppearanceBodyMedium\"\n                            android:textColor=\"?colorOnPrimary\" />\n                    </RelativeLayout>\n                </com.google.android.material.card.MaterialCardView>\n\n                <com.google.android.material.card.MaterialCardView\n                    android:id=\"@+id/warning_card\"\n                    style=\"@style/Widget.Material3.CardView.Outlined\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"16dp\"\n                    app:cardBackgroundColor=\"?colorErrorContainer\">\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\"\n                        android:padding=\"?dialogPreferredPadding\">\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/warning_title\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:textAppearance=\"?textAppearanceTitleMedium\"\n                            android:textColor=\"?colorOnErrorContainer\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/warning_summary\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:textAppearance=\"?textAppearanceBodyMedium\"\n                            android:textColor=\"?colorOnErrorContainer\" />\n\n                    </LinearLayout>\n                </com.google.android.material.card.MaterialCardView>\n\n                <com.google.android.material.card.MaterialCardView\n                    android:id=\"@+id/update_card\"\n                    style=\"@style/Widget.Material3.CardView.Outlined\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"16dp\"\n                    app:cardBackgroundColor=\"?colorSecondaryContainer\">\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\"\n                        android:padding=\"?dialogPreferredPadding\">\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/update_title\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:textAppearance=\"?textAppearanceTitleMedium\"\n                            android:textColor=\"?colorOnSecondaryContainer\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/update_summary\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:textAppearance=\"?textAppearanceBodyMedium\"\n                            android:textColor=\"?colorOnSecondaryContainer\" />\n\n                        <LinearLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_marginTop=\"?dialogPreferredPadding\"\n                            android:gravity=\"end\"\n                            android:orientation=\"horizontal\">\n\n                            <com.google.android.material.button.MaterialButton\n                                android:id=\"@+id/update_btn\"\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:hint=\"@string/install\"\n                                android:text=\"@string/install\"\n                                android:textAlignment=\"center\" />\n                        </LinearLayout>\n                    </LinearLayout>\n                </com.google.android.material.card.MaterialCardView>\n\n                <com.google.android.material.card.MaterialCardView\n                    android:id=\"@+id/developer_warning_card\"\n                    style=\"@style/Widget.Material3.CardView.Outlined\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"16dp\"\n                    android:visibility=\"gone\"\n                    app:cardBackgroundColor=\"?colorSecondaryContainer\">\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\"\n                        android:padding=\"?dialogPreferredPadding\">\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/developer_warning_title\"\n                            android:textAppearance=\"?textAppearanceTitleMedium\"\n                            android:textColor=\"?colorOnSecondaryContainer\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/developer_warning_summary\"\n                            android:textAppearance=\"?textAppearanceBodyMedium\"\n                            android:textColor=\"?colorOnSecondaryContainer\" />\n\n                    </LinearLayout>\n                </com.google.android.material.card.MaterialCardView>\n\n                <com.google.android.material.card.MaterialCardView\n                    android:id=\"@+id/info_card\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"16dp\">\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\"\n                        android:padding=\"?dialogPreferredPadding\">\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_api_version\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/api_version\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/not_installed\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/settings_xposed_api_call_protection\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/api\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/not_installed\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_dex2oat_wrapper\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/dex2oat_wrapper\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/not_installed\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_framework_version\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/framework_version\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/not_installed\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_manager_package_name\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/manager_package_name\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:gravity=\"center_vertical\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_system_version\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/system_version\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_device\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/device\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            style=\"@style/DeviceInfoDialogLabel\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/info_system_abi\" />\n\n                        <com.google.android.material.textview.MaterialTextView\n                            android:id=\"@+id/system_abi\"\n                            style=\"@style/DeviceInfoDialogValue\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:paddingBottom=\"0dp\" />\n\n                        <LinearLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:gravity=\"end\"\n                            android:orientation=\"horizontal\">\n\n                            <com.google.android.material.button.MaterialButton\n                                android:id=\"@+id/copy_info\"\n                                style=\"?borderlessButtonStyle\"\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:text=\"@string/copy\"\n                                android:textAlignment=\"center\"\n                                android:tooltipText=\"@string/copy\" />\n                        </LinearLayout>\n                    </LinearLayout>\n\n                </com.google.android.material.card.MaterialCardView>\n            </LinearLayout>\n        </rikka.widget.borderview.BorderNestedScrollView>\n    </FrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/fragment_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:animateLayoutChanges=\"true\"\n    app:consumeSystemWindowsInsets=\"@integer/internal_fragment_insets\"\n    app:edgeToEdge=\"true\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_insets\">\n\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"false\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_top_insets\">\n\n        <com.google.android.material.appbar.SubtitleCollapsingToolbarLayout\n            android:id=\"@+id/toolbar_layout\"\n            style=\"?attr/collapsingToolbarLayoutLargeStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/collapsingToolbarLayoutLargeSize\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleCollapseMode=\"scale\">\n\n            <View\n                android:id=\"@+id/click_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:elevation=\"0dp\"\n                android:theme=\"@style/ThemeOverlay.MaterialComponents.ActionBar\"\n                app:layout_collapseMode=\"pin\" />\n\n        </com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>\n\n        <com.google.android.material.tabs.TabLayout\n            android:id=\"@+id/tab_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/tab_layout_height\"\n            android:layout_gravity=\"bottom\"\n            android:background=\"@android:color/transparent\"\n            app:tabGravity=\"center\"\n            app:tabIndicatorAnimationMode=\"elastic\"\n            app:tabMode=\"scrollable\" />\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <androidx.viewpager2.widget.ViewPager2\n            android:id=\"@+id/view_pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n    </FrameLayout>\n\n    <com.google.android.material.floatingactionbutton.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end|bottom\"\n        android:layout_margin=\"?attr/dialogPreferredPadding\"\n        android:contentDescription=\"@string/add_module_to_user\"\n        android:src=\"@drawable/ic_baseline_add_24\"\n        android:tooltipText=\"@string/add_module_to_user\"\n        android:visibility=\"gone\"\n        app:backgroundTint=\"?attr/colorPrimary\"\n        app:layout_behavior=\"@string/hide_bottom_view_on_scroll_behavior\"\n        app:layout_fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\"\n        app:tint=\"?attr/colorOnPrimary\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_repo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/fragment_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:consumeSystemWindowsInsets=\"@integer/internal_fragment_insets\"\n    app:edgeToEdge=\"true\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_insets\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"false\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_top_insets\">\n\n        <com.google.android.material.appbar.SubtitleCollapsingToolbarLayout\n            android:id=\"@+id/toolbar_layout\"\n            style=\"?attr/collapsingToolbarLayoutLargeStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/collapsingToolbarLayoutLargeSize\"\n            app:forceApplySystemWindowInsetTop=\"true\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleCollapseMode=\"scale\">\n\n            <View\n                android:id=\"@+id/click_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:elevation=\"0dp\"\n                android:theme=\"@style/ThemeOverlay.MaterialComponents.ActionBar\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n            android:id=\"@+id/swipe_refresh_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <org.lsposed.manager.ui.widget.EmptyStateRecyclerView\n                android:id=\"@+id/recycler_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:clipToPadding=\"false\"\n                android:fadeScrollbars=\"true\"\n                android:scrollbarStyle=\"insideOverlay\"\n                android:scrollbars=\"vertical\"\n                app:borderBottomVisibility=\"never\"\n                app:borderTopDrawable=\"@null\"\n                app:borderTopVisibility=\"whenTop\"\n                app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\" />\n        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n    </FrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/fragment_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:consumeSystemWindowsInsets=\"@integer/internal_fragment_insets\"\n    app:edgeToEdge=\"true\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_insets\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:fitsSystemWindows=\"false\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_top_insets\">\n\n        <com.google.android.material.appbar.SubtitleCollapsingToolbarLayout\n            android:id=\"@+id/toolbar_layout\"\n            style=\"?attr/collapsingToolbarLayoutLargeStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/collapsingToolbarLayoutLargeSize\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleCollapseMode=\"scale\">\n\n            <View\n                android:id=\"@+id/click_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:elevation=\"0dp\"\n                android:theme=\"@style/ThemeOverlay.MaterialComponents.ActionBar\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/setting_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_log_textview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<com.google.android.material.textview.MaterialTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/log_item\"\n    style=\"@style/TextAppearance.AppCompat.Small\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:clipToPadding=\"false\"\n    android:fontFamily=\"monospace\"\n    android:hyphenationFrequency=\"fullFast\"\n    android:paddingHorizontal=\"@dimen/m3_appbar_expanded_title_margin_horizontal\"\n    android:textColor=\"?attr/colorOnSurface\"\n    android:textSize=\"12sp\" />\n"
  },
  {
    "path": "app/src/main/res/layout/item_master_switch.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<rikka.widget.mainswitchbar.MainSwitchBar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/master_switch\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:text=\"@string/enable_module\" />\n"
  },
  {
    "path": "app/src/main/res/layout/item_module.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:minHeight=\"?attr/listPreferredItemHeight\"\n    android:paddingVertical=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"24dp\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/item_root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <ImageView\n            android:id=\"@+id/app_icon\"\n            android:layout_width=\"@dimen/app_icon_size\"\n            android:layout_height=\"@dimen/app_icon_size\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            tools:ignore=\"ContentDescription\"\n            tools:srcCompat=\"@tools:sample/backgrounds/scenic\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"20dp\"\n            android:ellipsize=\"marquee\"\n            android:scrollbars=\"none\"\n            android:textAppearance=\"?android:attr/textAppearanceListItem\"\n            android:textIsSelectable=\"false\"\n            android:textSize=\"16sp\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/app_Package_Name\"\n            app:layout_constraintStart_toEndOf=\"@id/app_icon\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"LSPosed Manager\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/app_Package_Name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:maxLines=\"1\"\n            android:scrollbars=\"none\"\n            android:textAppearance=\"?android:attr/textAppearanceListItemSecondary\"\n            android:visibility=\"gone\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/app_Version_Name\"\n            app:layout_constraintEnd_toStartOf=\"@id/checkbox\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"@id/app_name\"\n            app:layout_constraintTop_toBottomOf=\"@id/app_name\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"com.lsposed.manager\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/app_Version_Name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:maxLines=\"1\"\n            android:scrollbars=\"none\"\n            android:textAppearance=\"?android:attr/textAppearanceListItemSecondary\"\n            android:visibility=\"gone\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/description\"\n            app:layout_constraintEnd_toStartOf=\"@id/checkbox\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"@id/app_Package_Name\"\n            app:layout_constraintTop_toBottomOf=\"@id/app_Package_Name\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"Version :1.8.1\" />\n\n        <org.lsposed.manager.ui.widget.ExpandableTextView\n            android:id=\"@+id/description\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:maxLines=\"5\"\n            android:scrollbars=\"none\"\n            android:textAlignment=\"viewStart\"\n            android:textAppearance=\"?android:attr/textAppearanceListItemSecondary\"\n            android:textColor=\"?android:attr/textColorSecondary\"\n            android:visibility=\"gone\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"@id/hint\"\n            app:layout_constraintEnd_toStartOf=\"@id/checkbox\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"@id/app_Version_Name\"\n            app:layout_constraintTop_toBottomOf=\"@id/app_Version_Name\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:ignore=\"RtlCompat\"\n            tools:text=\"A very very very very very very very very very very very very long string\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/hint\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:textAlignment=\"viewStart\"\n            android:textAppearance=\"?android:attr/textAppearanceListItemSecondary\"\n            android:textColor=\"?android:attr/textColorPrimary\"\n            android:visibility=\"gone\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toStartOf=\"@id/checkbox\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"@id/description\"\n            app:layout_constraintTop_toBottomOf=\"@id/description\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:ignore=\"RtlCompat\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/version_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:marqueeRepeatLimit=\"marquee_forever\"\n            android:maxWidth=\"100dp\"\n            android:singleLine=\"true\"\n            android:textAlignment=\"viewEnd\"\n            android:textAppearance=\"?android:attr/textAppearanceListItemSecondary\"\n            android:textColor=\"?android:attr/textColorSecondary\"\n            android:textIsSelectable=\"false\"\n            android:visibility=\"gone\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"1.0\"\n            app:layout_constraintStart_toEndOf=\"@id/app_name\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/last_names\" />\n\n        <com.google.android.material.checkbox.MaterialCheckBox\n            android:id=\"@+id/checkbox\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/transparent\"\n            android:clickable=\"false\"\n            android:focusable=\"false\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_onlinemodule.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:minHeight=\"?attr/listPreferredItemHeight\"\n    android:paddingVertical=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"24dp\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/item_root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\">\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:maxLines=\"1\"\n            android:scrollbars=\"none\"\n            android:textAppearance=\"?android:attr/textAppearanceListItem\"\n            android:textSize=\"16sp\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintBottom_toTopOf=\"@id/app_Package_Name\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/app_Package_Name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:maxLines=\"1\"\n            android:scrollbars=\"none\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/description\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/app_name\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <org.lsposed.manager.ui.widget.ExpandableTextView\n            android:id=\"@+id/description\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:hyphenationFrequency=\"fullFast\"\n            android:maxLines=\"6\"\n            android:minHeight=\"48dp\"\n            android:scrollbars=\"none\"\n            android:textAlignment=\"viewStart\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/hint\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/app_Package_Name\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:ignore=\"RtlCompat\"\n            tools:text=\"A very very very very very very very very very very very very \\n long string\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/hint\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:textAppearance=\"?android:attr/textAppearanceListItemSecondary\"\n            android:textColor=\"?android:attr/textColorPrimary\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/published_Time\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/description\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/published_Time\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_marginTop=\"2dp\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"\n            android:textSize=\"12sp\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"0.0\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/hint\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_repo_loadmore.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"64dp\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"?selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\">\n\n    <com.google.android.material.progressindicator.CircularProgressIndicator\n        android:id=\"@+id/progress\"\n        android:layout_width=\"36dp\"\n        android:layout_height=\"36dp\"\n        android:indeterminate=\"true\"\n        android:layout_gravity=\"center\"\n        android:visibility=\"gone\"\n        app:showAnimationBehavior=\"outward\"\n        app:hideAnimationBehavior=\"inward\" />\n\n    <com.google.android.material.textview.MaterialTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:drawableStart=\"@drawable/ic_keyboard_arrow_down\"\n        android:textAppearance=\"?textAppearanceListItem\"\n        android:textColor=\"?android:attr/textColorSecondary\"\n        android:drawablePadding=\"4dp\"\n        android:layout_gravity=\"center\"\n        android:text=\"@string/module_release_load_more\" />\n\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_repo_readme.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\"\n    android:clipToPadding=\"false\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\">\n\n    <rikka.widget.borderview.BorderNestedScrollView\n        android:id=\"@+id/scrollView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:borderBottomVisibility=\"never\"\n        app:borderTopDrawable=\"@null\"\n        app:borderTopVisibility=\"whenTop\">\n\n        <org.lsposed.manager.ui.widget.ScrollWebView\n            android:id=\"@+id/readme\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:padding=\"16dp\"\n            android:scrollbars=\"none\" />\n    </rikka.widget.borderview.BorderNestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_repo_recyclerview.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<org.lsposed.manager.ui.widget.EmptyStateRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/recycler_view\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:fadeScrollbars=\"true\"\n    android:scrollbarStyle=\"insideOverlay\"\n    android:scrollbars=\"vertical\"\n    app:borderBottomVisibility=\"never\"\n    app:borderTopDrawable=\"@null\"\n    app:borderTopVisibility=\"whenTop\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\" />\n"
  },
  {
    "path": "app/src/main/res/layout/item_repo_release.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?selectableItemBackground\"\n    android:clickable=\"true\"\n    android:clipChildren=\"false\"\n    android:clipToPadding=\"false\"\n    android:focusable=\"true\"\n    android:minHeight=\"?attr/listPreferredItemHeight\"\n    android:paddingVertical=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"24dp\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/item_root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"center\">\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textAppearance=\"?android:attr/textAppearanceListItem\"\n            android:textSize=\"16sp\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintBottom_toTopOf=\"@id/published_Time\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/published_Time\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"\n            android:textSize=\"14sp\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintBottom_toTopOf=\"@id/description\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/title\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <org.lsposed.manager.ui.widget.ScrollWebView\n            android:id=\"@+id/description\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:scrollbars=\"none\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"1.0\"\n            app:layout_constraintStart_toStartOf=\"@+id/published_Time\"\n            app:layout_constraintTop_toBottomOf=\"@id/published_Time\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <com.google.android.material.button.MaterialButton\n            android:id=\"@+id/view_assets\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:text=\"@string/module_release_view_assets\"\n            android:textAlignment=\"center\"\n            android:tooltipText=\"@string/module_release_view_assets\"\n            app:icon=\"@drawable/ic_attach_file\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"@id/description\"\n            app:layout_constraintTop_toBottomOf=\"@id/description\" />\n\n        <com.google.android.material.button.MaterialButton\n            android:id=\"@+id/open_in_browser\"\n            style=\"?attr/materialIconButtonOutlinedStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"6dp\"\n            android:layout_marginTop=\"4dp\"\n            android:textAlignment=\"center\"\n            android:tooltipText=\"@string/menu_open_in_browser\"\n            app:icon=\"@drawable/ic_open_in_browser\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@+id/view_assets\"\n            app:layout_constraintTop_toBottomOf=\"@id/description\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_repo_title_description.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:minHeight=\"?attr/listPreferredItemHeight\"\n    android:paddingVertical=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"24dp\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/item_root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\">\n\n        <com.google.android.material.textview.MaterialTextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textAppearance=\"?android:attr/textAppearanceListItem\"\n            android:textSize=\"16sp\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintBottom_toTopOf=\"@id/description\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n        <org.lsposed.manager.ui.widget.LinkifyTextView\n            android:id=\"@+id/description\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"@+id/title\"\n            app:layout_constraintTop_toBottomOf=\"@id/title\"\n            app:layout_constraintWidth_max=\"wrap\"\n            tools:text=\"@tools:sample/lorem\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/preference_recyclerview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<rikka.widget.borderview.BorderRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@android:id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:fadeScrollbars=\"true\"\n    android:scrollbarStyle=\"insideOverlay\"\n    android:scrollbars=\"vertical\"\n    app:borderBottomVisibility=\"never\"\n    app:borderTopDrawable=\"@null\"\n    app:borderTopVisibility=\"whenTop\"\n    app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\"\n    tools:ignore=\"UnusedResources\"\n    tools:viewBindingIgnore=\"true\" />\n"
  },
  {
    "path": "app/src/main/res/layout/scrollable_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<rikka.widget.borderview.BorderNestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <LinearLayout\n        android:id=\"@+id/dialog_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingHorizontal=\"?dialogPreferredPadding\"\n        android:paddingTop=\"?dialogPreferredPadding\">\n\n    </LinearLayout>\n</rikka.widget.borderview.BorderNestedScrollView>\n"
  },
  {
    "path": "app/src/main/res/layout/swiperefresh_recyclerview.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/swipe_refresh_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <org.lsposed.manager.ui.widget.EmptyStateRecyclerView\n        android:id=\"@+id/recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        android:fadeScrollbars=\"true\"\n        android:scrollbarStyle=\"insideOverlay\"\n        android:scrollbars=\"vertical\"\n        app:borderBottomVisibility=\"never\"\n        app:borderTopDrawable=\"@null\"\n        app:borderTopVisibility=\"whenTop\"\n        app:fitsSystemWindowsInsets=\"@integer/internal_fragment_bottom_insets\" />\n</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n"
  },
  {
    "path": "app/src/main/res/layout-sw600dp/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/nav_host_fragment\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:background=\"?android:colorBackground\"\n            app:defaultNavHost=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@id/nav\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:navGraph=\"@navigation/main_nav\" />\n\n        <com.google.android.material.navigationrail.NavigationRailView\n            android:id=\"@+id/nav\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:menu=\"@menu/navigation_menu\"\n            app:menuGravity=\"center\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/menu/context_menu_modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/menu_launch\"\n        android:title=\"@string/module_settings\" />\n    <item\n        android:id=\"@+id/menu_other_app\"\n        android:title=\"@string/modules_other_app\" />\n    <item\n        android:id=\"@+id/menu_repo\"\n        android:title=\"@string/view_in_repo\" />\n    <item\n        android:id=\"@+id/menu_app_info\"\n        android:title=\"@string/module_app_info\" />\n    <item\n        android:id=\"@+id/menu_compile_speed\"\n        android:title=\"@string/compile_speed\" />\n    <item\n        android:id=\"@+id/menu_uninstall\"\n        android:title=\"@string/module_uninstall\" />\n\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_app_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/menu_launch\"\n        android:title=\"@string/app_launch\" />\n    <item\n        android:id=\"@+id/menu_force_stop\"\n        android:title=\"@string/force_stop\" />\n    <item\n        android:id=\"@+id/menu_compile_speed\"\n        android:title=\"@string/compile_speed\" />\n    <item\n        android:id=\"@+id/menu_other_app\"\n        android:title=\"@string/modules_other_app\" />\n    <item\n        android:id=\"@+id/menu_app_info\"\n        android:title=\"@string/module_app_info\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_app_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <item\n        android:id=\"@+id/menu_search\"\n        android:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        android:icon=\"@drawable/ic_baseline_search_24\"\n        android:showAsAction=\"ifRoom|collapseActionView\"\n        android:title=\"@android:string/search_go\" />\n\n    <item\n        android:showAsAction=\"never\"\n        android:title=\"@string/menu_select\">\n        <menu>\n            <item\n                android:id=\"@+id/use_recommended\"\n                android:title=\"@string/use_recommended\" />\n            <item\n                android:id=\"@+id/select_all\"\n                android:title=\"@string/menu_select_all\" />\n            <item\n                android:id=\"@+id/select_none\"\n                android:title=\"@string/menu_select_none\" />\n            <item\n                android:id=\"@+id/auto_include\"\n                android:checkable=\"true\"\n                android:title=\"@string/menu_auto_include\" />\n        </menu>\n    </item>\n\n    <item\n        android:showAsAction=\"never\"\n        android:title=\"@string/menu_hide\">\n        <menu>\n            <item\n                android:id=\"@+id/item_filter_games\"\n                android:checkable=\"true\"\n                android:checked=\"true\"\n                android:title=\"@string/menu_show_games\" />\n            <item\n                android:id=\"@+id/item_filter_modules\"\n                android:checkable=\"true\"\n                android:checked=\"true\"\n                android:title=\"@string/menu_show_modules\" />\n            <item\n                android:id=\"@+id/item_filter_system\"\n                android:checkable=\"true\"\n                android:checked=\"true\"\n                android:title=\"@string/menu_show_system_apps\" />\n            <item\n                android:id=\"@+id/item_filter_denylist\"\n                android:checkable=\"true\"\n                android:checked=\"true\"\n                android:title=\"@string/menu_show_denylist\" />\n        </menu>\n    </item>\n\n    <item\n        android:showAsAction=\"never\"\n        android:title=\"@string/menu_sort\">\n        <menu>\n            <group android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/item_sort_by_name\"\n                    android:checked=\"true\"\n                    android:title=\"@string/sort_by_name\" />\n                <item\n                    android:id=\"@+id/item_sort_by_package_name\"\n                    android:title=\"@string/sort_by_package_name\" />\n                <item\n                    android:id=\"@+id/item_sort_by_install_time\"\n                    android:title=\"@string/sort_by_install_time\" />\n                <item\n                    android:id=\"@+id/item_sort_by_update_time\"\n                    android:title=\"@string/sort_by_update_time\" />\n            </group>\n            <item\n                android:id=\"@+id/reverse\"\n                android:checkable=\"true\"\n                android:title=\"@string/sort_reverse\" />\n        </menu>\n    </item>\n\n    <item\n        android:showAsAction=\"never\"\n        android:title=\"@string/menu_backup_and_restore\">\n        <menu>\n            <item\n                android:id=\"@+id/backup\"\n                android:title=\"@string/menu_backup\" />\n            <item\n                android:id=\"@+id/restore\"\n                android:title=\"@string/menu_restore\" />\n        </menu>\n    </item>\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_home.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/menu_issue\"\n        android:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        android:icon=\"@drawable/ic_baseline_chat_24\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/feedback_or_suggestion\" />\n\n    <item\n        android:id=\"@+id/menu_about\"\n        android:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        android:icon=\"@drawable/ic_baseline_info_24\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/About\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_logs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/menu_save\"\n        android:icon=\"@drawable/ic_save\"\n        android:showAsAction=\"ifRoom\"\n        android:title=\"@string/menuSaveToSd\" />\n\n    <item\n        android:id=\"@+id/menu_scroll_top\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/scroll_top\" />\n\n    <item\n        android:id=\"@+id/menu_scroll_down\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/scroll_bottom\" />\n\n    <item\n        android:id=\"@+id/menu_clear\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/menuClearLog\" />\n\n    <item\n        android:id=\"@+id/menu_word_wrap\"\n        android:checkable=\"true\"\n        android:checked=\"false\"\n        android:title=\"@string/menu_enable_word_wrap\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/menu_search\"\n        android:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        android:icon=\"@drawable/ic_baseline_search_24\"\n        android:showAsAction=\"ifRoom|collapseActionView\"\n        android:title=\"@android:string/search_go\" />\n\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_repo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <item\n        android:id=\"@+id/menu_search\"\n        android:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        android:icon=\"@drawable/ic_baseline_search_24\"\n        android:showAsAction=\"ifRoom|collapseActionView\"\n        android:title=\"@android:string/search_go\" />\n\n    <item\n        android:id=\"@+id/item_list_sort\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/menu_sort\">\n        <menu>\n            <group android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/item_sort_by_name\"\n                    android:checked=\"true\"\n                    android:title=\"@string/sort_by_name\" />\n\n                <item\n                    android:id=\"@+id/item_sort_by_update_time\"\n                    android:title=\"@string/sort_by_update_time\" />\n            </group>\n            <item\n                android:id=\"@+id/item_upgradable_first\"\n                android:checkable=\"true\"\n                android:title=\"@string/sort_upgradable_first\" />\n        </menu>\n    </item>\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_repo_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/menu_open_in_browser\"\n        android:title=\"@string/menu_open_in_browser\"\n        android:icon=\"@drawable/ic_open_in_browser\"\n        android:showAsAction=\"ifRoom\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/navigation_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/repo_nav\"\n        android:icon=\"@drawable/ic_get_app_checkable\"\n        android:title=\"@string/module_repo\" />\n\n    <item\n        android:id=\"@+id/modules_nav\"\n        android:icon=\"@drawable/ic_extension_checkable\"\n        android:title=\"@string/Modules\" />\n\n    <item\n        android:id=\"@+id/main_fragment\"\n        android:icon=\"@drawable/ic_home_checkable\"\n        android:title=\"@string/overview\" />\n\n    <item\n        android:id=\"@+id/logs_fragment\"\n        android:icon=\"@drawable/ic_assignment_checkable\"\n        android:title=\"@string/Logs\" />\n\n    <item\n        android:id=\"@+id/settings_fragment\"\n        android:icon=\"@drawable/ic_settings_checkable\"\n        android:title=\"@string/Settings\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu-sw600dp/navigation_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/main_fragment\"\n        android:icon=\"@drawable/ic_home_checkable\"\n        android:title=\"@string/overview\" />\n    <item\n        android:id=\"@+id/modules_nav\"\n        android:icon=\"@drawable/ic_extension_checkable\"\n        android:title=\"@string/Modules\" />\n\n    <item\n        android:id=\"@+id/repo_nav\"\n        android:icon=\"@drawable/ic_get_app_checkable\"\n        android:title=\"@string/module_repo\" />\n\n    <item\n        android:id=\"@+id/logs_fragment\"\n        android:icon=\"@drawable/ic_assignment_checkable\"\n        android:title=\"@string/Logs\" />\n\n    <item\n        android:id=\"@+id/settings_fragment\"\n        android:icon=\"@drawable/ic_settings_checkable\"\n        android:title=\"@string/Settings\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/navigation/main_nav.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/main_fragment\">\n    <fragment\n        android:id=\"@+id/main_fragment\"\n        android:name=\"org.lsposed.manager.ui.fragment.HomeFragment\"\n        android:label=\"main\" />\n    <fragment\n        android:id=\"@+id/logs_fragment\"\n        android:name=\"org.lsposed.manager.ui.fragment.LogsFragment\"\n        android:label=\"logs\" />\n    <fragment\n        android:id=\"@+id/settings_fragment\"\n        android:name=\"org.lsposed.manager.ui.fragment.SettingsFragment\"\n        android:label=\"settings\" />\n\n    <include app:graph=\"@navigation/modules_nav\" />\n    <include app:graph=\"@navigation/repo_nav\" />\n</navigation>\n"
  },
  {
    "path": "app/src/main/res/navigation/modules_nav.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/modules_nav\"\n    app:startDestination=\"@id/modules_fragment\">\n    <fragment\n        android:id=\"@+id/modules_fragment\"\n        android:name=\"org.lsposed.manager.ui.fragment.ModulesFragment\"\n        android:label=\"modules\">\n        <action\n            android:id=\"@+id/action_modules_fragment_to_app_list_fragment\"\n            app:destination=\"@id/app_list_fragment\"\n            app:enterAnim=\"@anim/fragment_enter\"\n            app:exitAnim=\"@anim/fragment_exit\"\n            app:popEnterAnim=\"@anim/fragment_enter_pop\"\n            app:popExitAnim=\"@anim/fragment_exit_pop\" />\n    </fragment>\n    <fragment\n        android:id=\"@+id/app_list_fragment\"\n        android:name=\"org.lsposed.manager.ui.fragment.AppListFragment\"\n        android:label=\"app_list\">\n        <argument\n            android:name=\"modulePackageName\"\n            app:argType=\"string\" />\n\n        <argument\n            android:name=\"moduleUserId\"\n            app:argType=\"integer\" />\n        <deepLink\n            android:id=\"@+id/repo_item_deeplink\"\n            app:uri=\"lsposed://module?modulePackageName={modulePackageName}&amp;moduleUserId={moduleUserId}\" />\n    </fragment>\n    <action\n        android:id=\"@+id/action_app_list_fragment_to_modules_fragment\"\n        app:destination=\"@id/modules_fragment\"\n        app:enterAnim=\"@anim/fragment_enter\"\n        app:exitAnim=\"@anim/fragment_exit\"\n        app:popEnterAnim=\"@anim/fragment_enter_pop\"\n        app:popExitAnim=\"@anim/fragment_exit_pop\"\n        app:popUpTo=\"@id/main_fragment\"\n        app:popUpToInclusive=\"true\" />\n</navigation>\n"
  },
  {
    "path": "app/src/main/res/navigation/repo_nav.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<navigation android:id=\"@+id/repo_nav\"\n    app:startDestination=\"@id/repo_fragment\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <fragment\n        android:id=\"@+id/repo_fragment\"\n        android:label=\"repo\"\n        android:name=\"org.lsposed.manager.ui.fragment.RepoFragment\">\n        <action\n            android:id=\"@+id/action_repo_fragment_to_repo_item_fragment\"\n            app:destination=\"@id/repo_item_fragment\"\n            app:enterAnim=\"@anim/fragment_enter\"\n            app:exitAnim=\"@anim/fragment_exit\"\n            app:popEnterAnim=\"@anim/fragment_enter_pop\"\n            app:popExitAnim=\"@anim/fragment_exit_pop\" />\n    </fragment>\n    <fragment\n        android:id=\"@+id/repo_item_fragment\"\n        android:name=\"org.lsposed.manager.ui.fragment.RepoItemFragment\"\n        android:label=\"repo_item\">\n        <argument\n            android:name=\"modulePackageName\"\n            app:argType=\"string\" />\n\n        <action\n            android:id=\"@+id/action_repo_item_fragment_to_repo_fragment\"\n            app:destination=\"@id/repo_fragment\"\n            app:enterAnim=\"@anim/fragment_enter\"\n            app:exitAnim=\"@anim/fragment_exit\"\n            app:popEnterAnim=\"@anim/fragment_enter_pop\"\n            app:popExitAnim=\"@anim/fragment_exit_pop\"\n            app:popUpTo=\"@id/main_fragment\"\n            app:popUpToInclusive=\"true\" />\n        <deepLink\n            android:id=\"@+id/repo_item_deeplink\"\n            app:uri=\"lsposed://repo?modulePackageName={modulePackageName}\" />\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"MissingTranslation\">\n\n    <string-array name=\"theme_texts\" translatable=\"false\">\n        <item>@string/dark_theme_off</item>\n        <item>@string/dark_theme_on</item>\n        <item>@string/dark_theme_follow_system</item>\n    </string-array>\n\n    <string-array name=\"theme_values\" translatable=\"false\">\n        <item>MODE_NIGHT_NO</item>\n        <item>MODE_NIGHT_YES</item>\n        <item>MODE_NIGHT_FOLLOW_SYSTEM</item>\n    </string-array>\n\n    <string-array name=\"color_values\" translatable=\"false\">\n        <item>SAKURA</item>\n        <item>MATERIAL_RED</item>\n        <item>MATERIAL_PINK</item>\n        <item>MATERIAL_PURPLE</item>\n        <item>MATERIAL_DEEP_PURPLE</item>\n        <item>MATERIAL_INDIGO</item>\n        <item>MATERIAL_BLUE</item>\n        <item>MATERIAL_LIGHT_BLUE</item>\n        <item>MATERIAL_CYAN</item>\n        <item>MATERIAL_TEAL</item>\n        <item>MATERIAL_GREEN</item>\n        <item>MATERIAL_LIGHT_GREEN</item>\n        <item>MATERIAL_LIME</item>\n        <item>MATERIAL_YELLOW</item>\n        <item>MATERIAL_AMBER</item>\n        <item>MATERIAL_ORANGE</item>\n        <item>MATERIAL_DEEP_ORANGE</item>\n        <item>MATERIAL_BROWN</item>\n        <item>MATERIAL_BLUE_GREY</item>\n    </string-array>\n\n    <string-array name=\"color_texts\" translatable=\"false\">\n        <item>@string/color_sakura</item>\n        <item>@string/color_red</item>\n        <item>@string/color_pink</item>\n        <item>@string/color_purple</item>\n        <item>@string/color_deep_purple</item>\n        <item>@string/color_indigo</item>\n        <item>@string/color_blue</item>\n        <item>@string/color_light_blue</item>\n        <item>@string/color_cyan</item>\n        <item>@string/color_teal</item>\n        <item>@string/color_green</item>\n        <item>@string/color_light_green</item>\n        <item>@string/color_lime</item>\n        <item>@string/color_yellow</item>\n        <item>@string/color_amber</item>\n        <item>@string/color_orange</item>\n        <item>@string/color_deep_orange</item>\n        <item>@string/color_brown</item>\n        <item>@string/color_blue_grey</item>\n    </string-array>\n\n    <string-array name=\"update_channel_texts\" translatable=\"false\">\n        <item>@string/update_channel_stable</item>\n        <item>@string/update_channel_bate</item>\n        <item>@string/update_channel_nightly</item>\n    </string-array>\n\n    <string-array name=\"update_channel_values\" translatable=\"false\">\n        <item>CHANNEL_STABLE</item>\n        <item>CHANNEL_BETA</item>\n        <item>CHANNEL_NIGHTLY</item>\n    </string-array>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n    <attr name=\"colorNormal\" format=\"color\" />\n    <attr name=\"colorInstall\" format=\"color\" />\n\n\n    <declare-styleable name=\"SubtitleCollapsingToolbarLayout\">\n        <!-- Specifies extra space on the start, top, end and bottom sides of the the expanded title text.\n             Margin values should be positive,\n             subtitle will also be affected. -->\n        <attr name=\"expandedTitleMargin\" />\n        <!-- Specifies extra space on the start side of the the expanded title text. Margin values should be positive,\n             subtitle will also be affected. -->\n        <attr name=\"expandedTitleMarginStart\" />\n        <!-- Specifies extra space on the top side of the the expanded title text. Margin values should be positive,\n             subtitle will also be affected. -->\n        <attr name=\"expandedTitleMarginTop\" />\n        <!-- Specifies extra space on the end side of the the expanded title text. Margin values should be positive,\n             subtitle will also be affected. -->\n        <attr name=\"expandedTitleMarginEnd\" />\n        <!-- Specifies extra space on the bottom side of the the expanded title text. Margin values should be positive,\n             subtitle will also be affected. -->\n        <attr name=\"expandedTitleMarginBottom\" />\n\n        <!-- The text appearance of the CollapsingToolbarLayout's title when it is fully 'expanded' -->\n        <attr name=\"expandedTitleTextAppearance\" />\n        <!-- The text appearance of the CollapsingToolbarLayout's subtitle when it is fully 'expanded' -->\n        <attr name=\"expandedSubtitleTextAppearance\" format=\"reference\" />\n        <!-- The text appearance of the CollapsingToolbarLayouts title when it is fully 'collapsed' -->\n        <attr name=\"collapsedTitleTextAppearance\" />\n        <!-- The text appearance of the CollapsingToolbarLayouts subtitle when it is fully 'collapsed' -->\n        <attr name=\"collapsedSubtitleTextAppearance\" format=\"reference\" />\n\n        <!-- The drawable to use as a scrim on top of the CollapsingToolbarLayouts\n             content when it has been scrolled sufficiently off screen. -->\n        <attr name=\"contentScrim\" />\n        <!-- The drawable to use as a scrim for the status bar content when the\n             CollapsingToolbarLayout has been scrolled sufficiently off screen.\n             Only works on Lollipop with the correct setup. -->\n        <attr name=\"statusBarScrim\" />\n        <!-- The id of the primary Toolbar child that you wish to use for the purpose of collapsing.\n             This Toolbar descendant view does not need to be a direct child of the layout.\n             If you do not set this, the first direct Toolbar child found will be used. -->\n        <attr name=\"toolbarId\" />\n        <!-- Specifies the amount of visible height in pixels used to define when to trigger a\n             scrim visibility change. -->\n        <attr name=\"scrimVisibleHeightTrigger\" />\n        <!-- Specifies the duration used for scrim visibility animations. -->\n        <attr name=\"scrimAnimationDuration\" />\n\n        <!-- Specifies how the title should be positioned when collapsed,\n             subtitle will also be affected. -->\n        <attr name=\"collapsedTitleGravity\" />\n        <!-- Specifies how the title should be positioned when expanded,\n             subtitle will also be affected. -->\n        <attr name=\"expandedTitleGravity\" />\n\n        <!-- Whether the CollapsingToolbarLayout should draw its own shrinking/growing title. -->\n        <attr name=\"titleEnabled\" />\n        <!-- The title to show when titleEnabled is set to true. -->\n        <attr name=\"title\" />\n        <!-- The subtitle to show when titleEnabled is set to true. -->\n        <attr name=\"subtitle\" />\n    </declare-styleable>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2023 LSPosed Contributors\n  -->\n\n<resources>\n    <color name=\"primary_text_material_inverse\">@color/abc_primary_text_material_dark</color>\n\n    <color name=\"primary_text_material\">@color/abc_primary_text_material_light</color>\n\n    <color name=\"ic_launcher_foreground\">#FFFFFF</color>\n    <color name=\"ic_launcher_background\">#F48FB1</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n    <dimen name=\"app_icon_size\">48dp</dimen>\n    <dimen name=\"tab_layout_height\">48dp</dimen>\n\n    <dimen name=\"home_primary_elevation\">6dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/integer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<resources>\n    <integer name=\"internal_fragment_insets\">0x00800007</integer> <!-- start|end -->\n    <integer name=\"internal_fragment_top_insets\">0x30</integer> <!-- top -->\n    <integer name=\"internal_fragment_bottom_insets\">0</integer> <!-- null -->\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<resources>\n    <bool name=\"show_system_settings\">false</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Overview</string>\n    <string name=\"Modules\">Modules</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d module enabled</item>\n        <item quantity=\"other\">%d modules enabled</item>\n    </plurals>\n    <string name=\"Logs\">Logs</string>\n    <string name=\"Settings\">Settings</string>\n    <string name=\"feedback_or_suggestion\">Feedback or suggestion</string>\n    <string name=\"About\">About</string>\n    <string name=\"report_issue\">Report issue</string>\n    <string name=\"module_repo\">Repository</string>\n    <string name=\"module_repo_up_to_date\">All modules up to date</string>\n    <string name=\"module_repo_published_time\">Published at %s</string>\n    <string name=\"module_repo_updated_time\">Updated at %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d module upgradable</item>\n        <item quantity=\"other\">%d modules upgradable</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Install</string>\n    <string name=\"install_summary\">Tap to install LSPosed</string>\n    <string name=\"not_installed\">Not installed</string>\n    <string name=\"not_install_summary\">LSPosed is not Installed</string>\n    <string name=\"activated\">Activated</string>\n    <string name=\"partial_activated\">Partially activated</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy is not loaded properly</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Some features will not work.<br/>Please report this to <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> developer.]]></string>\n    <string name=\"system_inject_fail_summary\">System Framework injection failed</string>\n    <string name=\"system_inject_fail\"><![CDATA[This is rare and may be caused by <b>Magisk</b> or some low-quality Magisk modules.<br/>Please try to disable Magisk modules other than Riru and LSPosed or submit full log to developers.]]></string>\n    <string name=\"system_prop_incorrect_summary\">System prop incorrect</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Some necessary system properties deleted or modified.<br/>Modules may invalidate occasionally.]]></string>\n    <string name=\"need_update\">Need to update</string>\n    <string name=\"please_update_summary\">Please install the latest version of LSPosed</string>\n    <string name=\"developer_warning_title\">Tips for module developer</string>\n    <string name=\"developer_warning_summary\">Please disable deploy optimizations on Android Studio, or use `gradlew installDebug` command to install. Otherwise the module apk will not be updated.</string>\n    <string name=\"info_api_version\">API version</string>\n    <string name=\"info_framework_version\">Framework version</string>\n    <string name=\"info_manager_package_name\">Manager package name</string>\n    <string name=\"info_system_version\">System version</string>\n    <string name=\"info_device\">Device</string>\n    <string name=\"info_system_abi\">System ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Enabled</string>\n    <string name=\"not_enabled\">Not enabled</string>\n    <string name=\"supported\">Supported</string>\n    <string name=\"unsupported\">Unsupported</string>\n    <string name=\"android_version_unsatisfied\">Android version unsatisfied</string>\n    <string name=\"crashed\">Crashed</string>\n    <string name=\"mount_failed\">Mount failed</string>\n    <string name=\"selinux_permissive\">SELinux is permissive</string>\n    <string name=\"sepolicy_incorrect\">SELinux policy is incorrect</string>\n    <string name=\"update_lsposed\">Update LSPosed</string>\n    <string name=\"update_lsposed_msg\">Confirm to update LSPosed? This device will reboot after update completion</string>\n    <string name=\"info_copied\">Copied to clipboard</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Welcome to LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">You are using the parasitic manager, which can create shortcut or still open from notification.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">You are using the parasitic manager, which can open from notification.</string>\n    <string name=\"create_shortcut\">Create shortcut</string>\n    <string name=\"never_show\">Never show</string>\n    <string name=\"app_welcome\">Parasitic Manager Recommended</string>\n    <string name=\"app_welcome_summary\">LSPosed now supports system parasitization to avoid detection, you can open parasitic manager from notification. It is recommended to uninstall the current application.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Save</string>\n    <string name=\"nav_item_logs_verbose\">Verbose Logs</string>\n    <string name=\"nav_item_logs_module\">Modules Logs</string>\n    <string name=\"logs_saving\">Saving log, please wait</string>\n    <string name=\"logs_saved\">Logs saved</string>\n    <string name=\"logs_save_failed2\">Failed to save:\\n%s</string>\n    <string name=\"menuClearLog\">Clear log now</string>\n    <string name=\"logs_cleared\">Log successfully cleared.</string>\n    <string name=\"scroll_top\">Scroll to top</string>\n    <string name=\"loading\">Loading…</string>\n    <string name=\"scroll_bottom\">Scroll to bottom</string>\n    <string name=\"menuReload\">Reload</string>\n    <string name=\"logs_clear_failed_2\">Failed to clear the log</string>\n    <string name=\"menu_enable_word_wrap\">Word Wrap</string>\n    <string name=\"enabled_verbose_log\">Verbose log enabled</string>\n    <string name=\"disabled_verbose_log\">Verbose log disabled</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(no description provided)</string>\n    <string name=\"warning_xposed_min_version\">This module requires a newer Xposed version (%d) and thus cannot be activated</string>\n    <string name=\"warning_target_version_higher\">This module is designed for a newer Xposed version (%d) and thus some functionalities may not work</string>\n    <string name=\"no_min_version_specified\">This module does not specify the Xposed version it needs.</string>\n    <string name=\"warning_min_version_too_low\">This module was created for Xposed version %1$d, but due to incompatible changes in version %2$d, it has been disabled</string>\n    <string name=\"warning_installed_on_external_storage\">This module cannot be loaded because it\\'s installed on the SD card, please move it to internal storage</string>\n    <string name=\"module_uninstall\">Uninstall</string>\n    <string name=\"module_settings\">Module settings</string>\n    <string name=\"view_in_repo\">View in Repo</string>\n    <string name=\"module_uninstall_message\">Do you want to uninstall this module?</string>\n    <string name=\"module_uninstalled\">Uninstalled %1$s</string>\n    <string name=\"module_uninstall_failed\">Uninstall unsuccessful</string>\n    <string name=\"add_module_to_user\">Add module to user</string>\n    <string name=\"module_installed\">Added %1$s to user %2$s</string>\n    <string name=\"module_install_failed\">Adding module failed</string>\n    <string name=\"install_to_user\">Install to user %s</string>\n    <string name=\"install_to_user_message\">Want to install %1$s to user %2$s? It is recommended to install manually, forcing installation via LSPosed may cause problems.</string>\n    <string name=\"expand\">expand</string>\n    <string name=\"collapse\">collapse</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Re-optimize</string>\n    <string name=\"compile_speed_msg\">Optimizing…</string>\n    <string name=\"compile_done\">Optimization complete</string>\n    <string name=\"app_launch\">Launch it</string>\n    <string name=\"compile_failed\">Optimization failed: return value is empty</string>\n    <string name=\"compile_failed_with_info\">Optimization failed: </string>\n    <string name=\"sort_by_name\">Application name</string>\n    <string name=\"sort_by_package_name\">Package name</string>\n    <string name=\"sort_by_install_time\">Install time</string>\n    <string name=\"sort_by_update_time\">Update time</string>\n    <string name=\"sort_reverse\">Reverse</string>\n    <string name=\"menu_show_system_apps\">System apps</string>\n    <string name=\"menu_sort\">Sorting</string>\n    <string name=\"enable_module\">Enable module</string>\n    <string name=\"no_scope_selected\">You did not select any app. Continue?</string>\n    <string name=\"menu_show_games\">Games</string>\n    <string name=\"menu_show_modules\">Modules</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Failed to save scope list</string>\n    <string name=\"app_version\">Version: %1$s</string>\n    <string name=\"menu_select\">Select</string>\n    <string name=\"use_recommended\">Recommended</string>\n    <string name=\"no_scope_selected_has_recommended\">You did not select any app. Select recommended apps?</string>\n    <string name=\"use_recommended_message\">Select recommended apps?</string>\n    <string name=\"menu_select_all\">All</string>\n    <string name=\"menu_select_none\">None</string>\n    <string name=\"menu_auto_include\">Auto-Include</string>\n    <string name=\"module_is_not_activated_yet\">Xposed module is not activated yet</string>\n    <string name=\"requested_by_module\">Recommended</string>\n    <string name=\"update_available\">Update available: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Module %s has been disabled since no app selected.</string>\n    <string name=\"android_framework\">System Framework</string>\n    <string name=\"menu_backup_and_restore\">Backup</string>\n    <string name=\"menu_backup\">Backup</string>\n    <string name=\"menu_restore\">Restore</string>\n    <string name=\"force_stop\">Force stop</string>\n    <string name=\"force_stop_dlg_title\">Force stop?</string>\n    <string name=\"force_stop_dlg_text\">If you force stop an app, it may misbehave.</string>\n    <string name=\"reboot_required\">Reboot is required for this change to apply</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Hide</string>\n    <string name=\"deny_list\">%s is on denylist. It may not take effect.</string>\n    <string name=\"deny_list_info\">On denylist</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">View in other app</string>\n    <string name=\"module_app_info\">App info</string>\n    <string name=\"list_empty\">¯\\\\_(ツ)_\\/¯\\nNothing here</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Disable verbose logs</string>\n    <string name=\"settings_disable_verbose_log_summary\">Verbose logs are required to report issues</string>\n    <string name=\"settings_enable_log_watchdog\">Enable log watchdog</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Log watchdog of LSPosed modifies system properties, which could be exploited to detect LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Black dark theme</string>\n    <string name=\"pure_black_dark_theme_summary\">Use the pure black theme if dark theme is enabled</string>\n    <string name=\"settings_group_theme\">Theme</string>\n    <string name=\"settings_backup_and_restore\">Backup and restore</string>\n    <string name=\"settings_backup_summery\">Backup module list and scope lists.</string>\n    <string name=\"settings_restore_summery\">Restore module list and scope lists.</string>\n    <string name=\"settings_backup\">Backup</string>\n    <string name=\"settings_backup_failed2\">Failed to backup:\\n%s</string>\n    <string name=\"enable_documentui\">Please enable DocumentUI</string>\n    <string name=\"settings_restore\">Restore</string>\n    <string name=\"settings_restore_failed2\">Failed to restore:\\n%s</string>\n    <string name=\"group_network\">Network</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">Workaround DNS poisoning in some nations</string>\n    <string name=\"theme_color\">Theme color</string>\n    <string name=\"theme_color_system\">System theme color</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Force apps to show launcher icons</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">After Android 10, apps are not allowed to hide their launcher icons. Turn off the toggle to disable this system feature.</string>\n    <string name=\"settings_group_system\">System</string>\n    <string name=\"settings_language\">Language</string>\n    <string name=\"settings_translation_contributors\">Translation contributors</string>\n    <string name=\"settings_translation\">Participate in translation</string>\n    <string name=\"settings_translation_summary\">Help us translate %s into your language</string>\n    <string name=\"settings_create_shortcut_summary\">Create a shortcut that can open parasitic manager</string>\n    <string name=\"settings_shortcut_pinned_hint\">Shortcut pinned</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">The current default launcher does not support pin shortcuts</string>\n    <string name=\"settings_enable_status_notification\">Status Notification</string>\n    <string name=\"settings_enable_status_notification_summary\">Show a notification that can open parasitic manager</string>\n    <string name=\"disable_status_notification_error\">No shortcut, cannot disable notification</string>\n    <string name=\"settings_update_channel\">Update channel</string>\n    <string name=\"update_channel_stable\">Stable</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly build</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API call protection</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Block dynamically loaded module code to use Xposed API, this may break some modules but benefit security</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Releases</string>\n    <string name=\"module_information\">Info</string>\n    <string name=\"module_information_homepage\">Homepage</string>\n    <string name=\"module_information_source_url\">Source code</string>\n    <string name=\"module_information_collaborators\">Collaborators</string>\n    <string name=\"module_release_view_assets\">Assets</string>\n    <string name=\"menu_open_in_browser\">Open in browser</string>\n    <string name=\"module_release_load_more\">Show older versions</string>\n    <string name=\"module_release_no_more\">No more release</string>\n    <string name=\"repo_load_failed\">Failed to load module repo: %s</string>\n    <string name=\"sort_upgradable_first\">Upgradable first</string>\n    <string name=\"installed\">Installed</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d download</item>\n        <item quantity=\"other\">%d downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Red</string>\n    <string name=\"color_pink\">Pink</string>\n    <string name=\"color_purple\">Purple</string>\n    <string name=\"color_deep_purple\">Deep purple</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blue</string>\n    <string name=\"color_light_blue\">Light blue</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">Green</string>\n    <string name=\"color_light_green\">Light green</string>\n    <string name=\"color_lime\">Lime</string>\n    <string name=\"color_yellow\">Yellow</string>\n    <string name=\"color_amber\">Amber</string>\n    <string name=\"color_orange\">Orange</string>\n    <string name=\"color_deep_orange\">Deep orange</string>\n    <string name=\"color_brown\">Brown</string>\n    <string name=\"color_blue_grey\">Blue grey</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings_untranslatable.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n    <string name=\"app_name\" translatable=\"false\">LSPosed</string>\n    <string name=\"install_url\" translatable=\"false\">https://github.com/JingMatrix/LSPosed#install</string>\n    <string name=\"latest_url\" translatable=\"false\">https://github.com/JingMatrix/LSPosed/releases/latest</string>\n    <string name=\"settings_group_repo\" translatable=\"false\">@string/module_repo</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n\n    <style name=\"AppTheme\" parent=\"Theme.Light\" />\n\n    <!-- SubtitleCollapsingToolbarLayout styles -->\n    <style name=\"Widget.Design.SubtitleCollapsingToolbar\" parent=\"Widget.Design.CollapsingToolbar\" />\n\n    <style name=\"TextAppearance.Design.SubtitleCollapsingToolbar.ExpandedTitle\" parent=\"TextAppearance.Design.CollapsingToolbar.Expanded\" />\n\n    <style name=\"TextAppearance.Design.SubtitleCollapsingToolbar.ExpandedSubtitle\" parent=\"TextAppearance.AppCompat.Title\">\n        <item name=\"android:textColor\">?android:attr/textColorSecondary</item>\n    </style>\n\n    <style name=\"language_menu_style\" parent=\"ThemeOverlay.Preference.SimpleMenuPreference.PopupMenu\">\n        <item name=\"android:textAlignment\">textStart</item>\n    </style>\n    <style name=\"Widget.Button.TextButton.Dialog.FullWidth\" parent=\"Widget.Material3.Button.TextButton.Dialog.Flush\">\n        <item name=\"android:layout_width\">0dp</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:maxWidth\">@null</item>\n    </style>\n    <style name=\"ThemeOverlay.MaterialAlertDialog.Centered.FullWidthButtons\" parent=\"ThemeOverlay.Material3.MaterialAlertDialog.Centered\">\n        <!-- Mark spacer as gone when showing full width buttons -->\n        <item name=\"materialAlertDialogButtonSpacerVisibility\">2</item>\n        <item name=\"buttonBarPositiveButtonStyle\">@style/Widget.Button.TextButton.Dialog.FullWidth</item>\n        <item name=\"buttonBarNegativeButtonStyle\">@style/Widget.Button.TextButton.Dialog.FullWidth</item>\n        <item name=\"buttonBarNeutralButtonStyle\">@style/Widget.Button.TextButton.Dialog.FullWidth</item>\n    </style>\n    <style name=\"ThemeOverlay.MaterialAlertDialog.FullWidthButtons\" parent=\"ThemeOverlay.Material3.MaterialAlertDialog\">\n        <!-- Mark spacer as gone when showing full width buttons -->\n        <item name=\"materialAlertDialogButtonSpacerVisibility\">2</item>\n        <item name=\"buttonBarPositiveButtonStyle\">@style/Widget.Button.TextButton.Dialog.FullWidth</item>\n        <item name=\"buttonBarNegativeButtonStyle\">@style/Widget.Button.TextButton.Dialog.FullWidth</item>\n        <item name=\"buttonBarNeutralButtonStyle\">@style/Widget.Button.TextButton.Dialog.FullWidth</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n\n    <style name=\"Base.AppTheme.Light\" parent=\"Theme.Material3.DynamicColors.Light.Rikka\">\n        <item name=\"android:windowLightStatusBar\">true</item>\n        <item name=\"colorPrimaryVariant\">@color/primary_dark_light_light_status_bar</item>\n\n        <item name=\"android:statusBarColor\">?android:colorBackground</item>\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n\n        <!-- Preference title -->\n        <item name=\"android:textAppearanceListItem\">?textAppearanceSubtitle1</item>\n        <!-- Preference category title -->\n        <item name=\"android:colorAccent\">?colorPrimary</item>\n        <!-- Preference switch -->\n        <item name=\"android:colorControlActivated\">?colorPrimary</item>\n    </style>\n\n    <style name=\"Theme.Light\" parent=\"Base.AppTheme.Light\" />\n\n    <style name=\"Base.AppTheme\" parent=\"Theme.Material3.DynamicColors.Dark.Rikka\">\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n\n        <!-- Preference title -->\n        <item name=\"android:textAppearanceListItem\">?textAppearanceSubtitle1</item>\n        <!-- Preference category title -->\n        <item name=\"android:colorAccent\">?colorPrimary</item>\n        <!-- Preference switch -->\n        <item name=\"android:colorControlActivated\">?colorPrimary</item>\n    </style>\n\n    <style name=\"Theme\" parent=\"Base.AppTheme\" />\n\n    <style name=\"DeviceInfoDialogLabel\">\n        <item name=\"android:textAlignment\">viewStart</item>\n        <item name=\"android:textAppearance\">@style/TextAppearance.MaterialComponents.Body1</item>\n        <item name=\"android:textColor\">?android:attr/textColorSecondary</item>\n    </style>\n\n    <style name=\"DeviceInfoDialogValue\">\n        <item name=\"android:textAlignment\">viewStart</item>\n        <item name=\"android:textAppearance\">@style/TextAppearance.MaterialComponents.Body2</item>\n        <item name=\"android:paddingBottom\">24dp</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n    <style name=\"ThemeOverlay.MaterialRed\" parent=\"ThemeOverlay.Light.MaterialRed\"/>\n    <style name=\"ThemeOverlay.MaterialPink\" parent=\"ThemeOverlay.Light.MaterialPink\"/>\n    <style name=\"ThemeOverlay.MaterialPurple\" parent=\"ThemeOverlay.Light.MaterialPurple\"/>\n    <style name=\"ThemeOverlay.MaterialDeepPurple\" parent=\"ThemeOverlay.Light.MaterialDeepPurple\"/>\n    <style name=\"ThemeOverlay.MaterialIndigo\" parent=\"ThemeOverlay.Light.MaterialIndigo\"/>\n    <style name=\"ThemeOverlay.MaterialBlue\" parent=\"ThemeOverlay.Light.MaterialBlue\"/>\n    <style name=\"ThemeOverlay.MaterialLightBlue\" parent=\"ThemeOverlay.Light.MaterialLightBlue\"/>\n    <style name=\"ThemeOverlay.MaterialCyan\" parent=\"ThemeOverlay.Light.MaterialCyan\"/>\n    <style name=\"ThemeOverlay.MaterialTeal\" parent=\"ThemeOverlay.Light.MaterialTeal\"/>\n    <style name=\"ThemeOverlay.MaterialGreen\" parent=\"ThemeOverlay.Light.MaterialGreen\"/>\n    <style name=\"ThemeOverlay.MaterialLightGreen\" parent=\"ThemeOverlay.Light.MaterialLightGreen\"/>\n    <style name=\"ThemeOverlay.MaterialLime\" parent=\"ThemeOverlay.Light.MaterialLime\"/>\n    <style name=\"ThemeOverlay.MaterialYellow\" parent=\"ThemeOverlay.Light.MaterialYellow\"/>\n    <style name=\"ThemeOverlay.MaterialAmber\" parent=\"ThemeOverlay.Light.MaterialAmber\"/>\n    <style name=\"ThemeOverlay.MaterialOrange\" parent=\"ThemeOverlay.Light.MaterialOrange\"/>\n    <style name=\"ThemeOverlay.MaterialDeepOrange\" parent=\"ThemeOverlay.Light.MaterialDeepOrange\"/>\n    <style name=\"ThemeOverlay.MaterialBrown\" parent=\"ThemeOverlay.Light.MaterialBrown\"/>\n    <style name=\"ThemeOverlay.MaterialBlueGrey\" parent=\"ThemeOverlay.Light.MaterialBlueGrey\"/>\n    <style name=\"ThemeOverlay.MaterialSakura\" parent=\"ThemeOverlay.Light.MaterialSakura\"/>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes_overlay.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources>\n\n    <style name=\"ThemeOverlay\" />\n\n    <style name=\"ThemeOverlay.Black\">\n        <item name=\"colorSurface\">@android:color/black</item>\n        <item name=\"android:colorBackground\">@android:color/black</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes_override.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"PrivateResource,UnusedResources\">\n\n    <!-- Add android:colorControlNormal -->\n    <style name=\"Base.ThemeOverlay.AppCompat.ActionBar\" tools:override=\"true\">\n        <item name=\"android:colorControlNormal\">?android:attr/textColorPrimary</item>\n        <item name=\"colorControlNormal\">?android:attr/textColorPrimary</item>\n        <item name=\"searchViewStyle\">@style/Widget.AppCompat.SearchView.ActionBar</item>\n    </style>\n\n    <style name=\"Base.ThemeOverlay.AppCompat.Dark.ActionBar\" tools:override=\"true\">\n        <item name=\"android:colorControlNormal\">?android:attr/textColorPrimary</item>\n        <item name=\"colorControlNormal\">?android:attr/textColorPrimary</item>\n        <item name=\"searchViewStyle\">@style/Widget.AppCompat.SearchView.ActionBar</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.CompoundButton.CheckBox\" parent=\"Widget.AppCompat.CompoundButton.CheckBox\" tools:override=\"true\">\n        <item name=\"enforceMaterialTheme\">true</item>\n        <item name=\"useMaterialThemeColors\">true</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.CompoundButton.RadioButton\" parent=\"Widget.AppCompat.CompoundButton.RadioButton\" tools:override=\"true\">\n        <item name=\"enforceMaterialTheme\">true</item>\n        <item name=\"useMaterialThemeColors\">true</item>\n    </style>\n\n    <style name=\"ThemeOverlay.Material.Dialog.ColorPicker\" parent=\"@style/ThemeOverlay.Material3.MaterialAlertDialog.Centered\" tools:override=\"true\" />\n\n    <!-- Base themes -->\n    <style name=\"Base.Light\" parent=\"Theme.Material3.Light\" tools:override=\"true\">\n        <item name=\"android:statusBarColor\">?android:colorBackground</item>\n        <item name=\"android:ambientShadowAlpha\">@dimen/ambient_shadow_alpha</item>\n        <item name=\"android:spotShadowAlpha\">@dimen/spot_shadow_alpha</item>\n\n        <item name=\"colorPrimary\">@color/primary_light</item>\n        <item name=\"colorPrimaryVariant\">@color/primary_dark_light</item>\n        <item name=\"swipeRefreshLayoutProgressSpinnerBackgroundColor\">?cardBackground</item>\n\n        <item name=\"colorAlert\">@color/material_red_A200</item>\n        <item name=\"colorSafe\">@color/material_green_600</item>\n        <item name=\"colorWarning\">@color/material_orange_700</item>\n        <item name=\"colorInactive\" tools:ignore=\"PrivateResource\">@color/material_grey_600</item>\n\n        <item name=\"navigationBarDividerColor\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"Base\" parent=\"Theme.Material3.Dark\" tools:override=\"true\">\n        <item name=\"android:statusBarColor\">?android:colorBackground</item>\n        <item name=\"android:ambientShadowAlpha\">@dimen/ambient_shadow_alpha</item>\n        <item name=\"android:spotShadowAlpha\">@dimen/spot_shadow_alpha</item>\n\n        <item name=\"colorPrimary\">@color/primary_dark</item>\n        <item name=\"colorPrimaryVariant\">@color/primary_dark_dark</item>\n        <item name=\"swipeRefreshLayoutProgressSpinnerBackgroundColor\">?cardBackground</item>\n\n        <item name=\"colorAlert\">@color/material_red_A100</item>\n        <item name=\"colorSafe\">@color/material_green_A100</item>\n        <item name=\"colorWarning\">@color/material_orange_A100</item>\n\n        <item name=\"navigationBarDividerColor\">@android:color/transparent</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-af/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Oorsig</string>\n    <string name=\"Modules\">Modules</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d module enabled</item>\n        <item quantity=\"other\">%d module enabled</item>\n    </plurals>\n    <string name=\"Logs\">Logs</string>\n    <string name=\"Settings\">Settings</string>\n    <string name=\"feedback_or_suggestion\">Terugvoer of voorstel</string>\n    <string name=\"About\">Oor</string>\n    <string name=\"report_issue\">Rapporteer probleem</string>\n    <string name=\"module_repo\">Bewaarplek</string>\n    <string name=\"module_repo_up_to_date\">Alle modules op datum</string>\n    <string name=\"module_repo_published_time\">Published at %s</string>\n    <string name=\"module_repo_updated_time\">Opgedateer op %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d module opgradeerbaar</item>\n        <item quantity=\"other\">%d modules opgradeerbaar</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Bekyk bronkode by %1$s<br/>Sluit aan by ons %2$s -kanaal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Installeer2</string>\n    <string name=\"install_summary\">Tik om LSPosed te installeer</string>\n    <string name=\"not_installed\">Not installed1</string>\n    <string name=\"not_install_summary\">LSPosed is nie geïnstalleer nie</string>\n    <string name=\"activated\">Activated</string>\n    <string name=\"partial_activated\">Partially activated</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy is nie behoorlik gelaai nie</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Sommige kenmerke sal nie werk nie.<br/>Rapporteer dit asseblief aan <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> ontwikkelaar.]]></string>\n    <string name=\"system_inject_fail_summary\">Stelselraamwerk-inspuiting het misluk</string>\n    <string name=\"system_inject_fail\"><![CDATA[Dit is skaars en kan veroorsaak word deur <b>Magisk</b> of sommige Magisk-modules van lae gehalte.<br/>Probeer asseblief om Magisk-modules anders as Riru en LSPosed te deaktiveer of dien volledige log aan ontwikkelaars in.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Stelselstut is verkeerd</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Sommige nodige stelseleienskappe is uitgevee of gewysig.<br/>Modules kan soms ongeldig word.]]></string>\n    <string name=\"need_update\">Need to update</string>\n    <string name=\"please_update_summary\">Please install the latest version of LSPosed</string>\n    <string name=\"info_api_version\">API version</string>\n    <string name=\"info_framework_version\">Framework version</string>\n    <string name=\"info_manager_package_name\">Bestuurder pakket naam</string>\n    <string name=\"info_system_version\">Stelsel weergawe</string>\n    <string name=\"info_device\">Toestel</string>\n    <string name=\"info_system_abi\">Stelsel ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Geaktiveer</string>\n    <string name=\"not_enabled\">Nie geaktiveer nie</string>\n    <string name=\"supported\">Ondersteun</string>\n    <string name=\"unsupported\">Ongesteun</string>\n    <string name=\"android_version_unsatisfied\">Android-weergawe nie tevrede nie</string>\n    <string name=\"crashed\">Het neergestort</string>\n    <string name=\"mount_failed\">Montering het misluk</string>\n    <string name=\"selinux_permissive\">SELinux is permissief</string>\n    <string name=\"sepolicy_incorrect\">SELinux-beleid is verkeerd</string>\n    <string name=\"update_lsposed\">Dateer LSPosed op</string>\n    <string name=\"update_lsposed_msg\">Bevestig om LSPosed op te dateer? Hierdie toestel sal herlaai nadat die opdatering voltooi is</string>\n    <string name=\"info_copied\">Gekopieer na knipbord</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Welkom by LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Jy gebruik die parasitiese bestuurder, wat kortpad kan skep of steeds oopmaak vanaf kennisgewing.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Jy gebruik die parasitiese bestuurder, wat kan oopmaak vanaf kennisgewing.</string>\n    <string name=\"create_shortcut\">Skep kortpad</string>\n    <string name=\"never_show\">Moet nooit wys nie</string>\n    <string name=\"app_welcome\">Parasitiese Bestuurder Aanbeveel</string>\n    <string name=\"app_welcome_summary\">LSPosed ondersteun nou stelselparasitering om opsporing te vermy, jy kan parasitiese bestuurder oopmaak vanaf kennisgewing. Dit word aanbeveel om die huidige toepassing te verwyder.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Stoor</string>\n    <string name=\"nav_item_logs_verbose\">Uitgebreide logs</string>\n    <string name=\"nav_item_logs_module\">Modules</string>\n    <string name=\"logs_saving\">Stoor tans logboek, wag asseblief</string>\n    <string name=\"logs_saved\">Logs gestoor</string>\n    <string name=\"logs_save_failed2\">Kon nie stoor nie:\\n%s</string>\n    <string name=\"menuClearLog\">Vee logboek nou uit</string>\n    <string name=\"logs_cleared\">Log is suksesvol uitgevee.</string>\n    <string name=\"scroll_top\">Blaai na bo</string>\n    <string name=\"loading\">Laai…</string>\n    <string name=\"scroll_bottom\">Blaai na onder</string>\n    <string name=\"menuReload\">Herlaai</string>\n    <string name=\"logs_clear_failed_2\">Kon nie die logboek skoonmaak nie</string>\n    <string name=\"menu_enable_word_wrap\">Woordomhulsel</string>\n    <string name=\"enabled_verbose_log\">Uitgebreide logboek geaktiveer</string>\n    <string name=\"disabled_verbose_log\">Uitgebreide logboek gedeaktiveer</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(geen beskrywing verskaf nie)</string>\n    <string name=\"warning_xposed_min_version\">Hierdie module vereis \\'n nuwer Xposed weergawe (%d) en kan dus nie geaktiveer word nie</string>\n    <string name=\"warning_target_version_higher\">This module is designed for a newer Xposed version (%d) and thus some functionalities may not work</string>\n    <string name=\"no_min_version_specified\">Hierdie module spesifiseer nie die Xposed-weergawe wat dit benodig nie.</string>\n    <string name=\"warning_min_version_too_low\">Hierdie module is geskep vir Xposed weergawe %1$d, maar as gevolg van onversoenbare veranderinge in weergawe %2$d, is dit gedeaktiveer</string>\n    <string name=\"warning_installed_on_external_storage\">Hierdie module kan nie gelaai word nie omdat dit op die SD-kaart geïnstalleer is, skuif dit asseblief na interne berging</string>\n    <string name=\"module_uninstall\">Deïnstalleer</string>\n    <string name=\"module_settings\">Module enabled</string>\n    <string name=\"view_in_repo\">Kyk in Repo</string>\n    <string name=\"module_uninstall_message\">Wil jy hierdie module deïnstalleer?</string>\n    <string name=\"module_uninstalled\">Deïnstalleer %1$s</string>\n    <string name=\"module_uninstall_failed\">Deïnstalleer onsuksesvol</string>\n    <string name=\"add_module_to_user\">%d module enabled</string>\n    <string name=\"module_installed\">Het %1$s by gebruiker %2$sgevoeg</string>\n    <string name=\"module_install_failed\">%d module enabled</string>\n    <string name=\"install_to_user\">Installeer op gebruiker %s</string>\n    <string name=\"install_to_user_message\">Wil jy %1$s op gebruiker %2$sinstalleer? Dit word aanbeveel om met die hand te installeer, om installasie via LSPosed te dwing, kan probleme veroorsaak.</string>\n    <string name=\"expand\">uitbrei</string>\n    <string name=\"collapse\">inval</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Heroptimaliseer</string>\n    <string name=\"compile_speed_msg\">Optimaliseer…</string>\n    <string name=\"compile_done\">Optimering voltooi</string>\n    <string name=\"app_launch\">Begin dit</string>\n    <string name=\"compile_failed\">Optimalisering het misluk: terugkeerwaarde is leeg</string>\n    <string name=\"compile_failed_with_info\">Optimalisering het misluk: </string>\n    <string name=\"sort_by_name\">Aansoeknaam</string>\n    <string name=\"sort_by_package_name\">Pakketnaam</string>\n    <string name=\"sort_by_install_time\">Installeer tyd</string>\n    <string name=\"sort_by_update_time\">Dateer tyd op</string>\n    <string name=\"sort_reverse\">Omgekeerde</string>\n    <string name=\"menu_show_system_apps\">Stelseltoepassings</string>\n    <string name=\"menu_sort\">Sorteer</string>\n    <string name=\"enable_module\">Aktiveer module</string>\n    <string name=\"no_scope_selected\">Jy het geen toepassing gekies nie. Aanhou?</string>\n    <string name=\"menu_show_games\">Speletjies</string>\n    <string name=\"menu_show_modules\">Modules</string>\n    <string name=\"menu_show_denylist\">Ontkenlys</string>\n    <string name=\"failed_to_save_scope_list\">Kon nie omvanglys stoor nie</string>\n    <string name=\"app_version\">weergawe: %1$s</string>\n    <string name=\"use_recommended\">Aanbeveel</string>\n    <string name=\"no_scope_selected_has_recommended\">Jy het geen toepassing gekies nie. Kies aanbevole programme?</string>\n    <string name=\"use_recommended_message\">Kies aanbevole programme?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed-module is nog nie geaktiveer nie</string>\n    <string name=\"requested_by_module\">Aanbeveel</string>\n    <string name=\"update_available\">Opdatering beskikbaar: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Module %s is gedeaktiveer aangesien geen toepassing gekies is nie.</string>\n    <string name=\"android_framework\">Stelselraamwerk</string>\n    <string name=\"menu_backup_and_restore\">Ondersteuning</string>\n    <string name=\"menu_backup\">Ondersteuning</string>\n    <string name=\"menu_restore\">Herstel</string>\n    <string name=\"force_stop\">Forseer om te stop</string>\n    <string name=\"force_stop_dlg_title\">Forseer om te stop?</string>\n    <string name=\"force_stop_dlg_text\">As jy \\'n program dwing om te stop, kan dit dalk wangedra.</string>\n    <string name=\"reboot_required\">Herselflaai word vereis vir hierdie verandering om van toepassing te wees</string>\n    <string name=\"reboot\">Herlaai</string>\n    <string name=\"menu_hide\">Versteek</string>\n    <string name=\"deny_list\">%s is op ontkenlys. Dit mag dalk nie in werking tree nie.</string>\n    <string name=\"deny_list_info\">Op ontkenlys</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Bekyk in \\'n ander toepassing</string>\n    <string name=\"module_app_info\">App inligting</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nNiks hier nie</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Raamwerk</string>\n    <string name=\"settings_disable_verbose_log\">Deaktiveer verbose logs</string>\n    <string name=\"settings_disable_verbose_log_summary\">Rapporteer kwessies versoek om verbose logs in te sluit</string>\n    <string name=\"pure_black_dark_theme\">Swart donker tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Gebruik die suiwer swart tema as donker tema geaktiveer is</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Friends en herstel</string>\n    <string name=\"settings_backup_summery\">Rugsteunmodulelys en omvanglyste.</string>\n    <string name=\"settings_restore_summery\">Herstel modulelys en omvanglyste.</string>\n    <string name=\"settings_backup\">Ondersteuning</string>\n    <string name=\"settings_backup_failed2\">Kon nie rugsteun nie:\\n%s</string>\n    <string name=\"enable_documentui\">Aktiveer asseblief DocumentUI</string>\n    <string name=\"settings_restore\">Herstel</string>\n    <string name=\"settings_restore_failed2\">Kon nie herstel nie:\\n%s</string>\n    <string name=\"group_network\">Netwerk</string>\n    <string name=\"dns_over_http\">DNS oor HTTPS</string>\n    <string name=\"dns_over_http_summary\">Oplossing DNS-vergiftiging in sommige lande</string>\n    <string name=\"theme_color\">Tema kleur</string>\n    <string name=\"theme_color_system\">Stelsel tema kleur</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Dwing programme om lanseerder-ikone te wys</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Ná Android 10 word programme nie toegelaat om hul lanseerder-ikone te versteek nie. Skakel die skakelaar af om hierdie stelselkenmerk te deaktiveer.</string>\n    <string name=\"settings_group_system\">Stelsel</string>\n    <string name=\"settings_language\">Taal</string>\n    <string name=\"settings_translation_contributors\">Vertaling bydraers</string>\n    <string name=\"settings_translation\">Neem deel aan vertaling</string>\n    <string name=\"settings_translation_summary\">Help ons om %s in jou taal te vertaal</string>\n    <string name=\"settings_create_shortcut_summary\">Skep \\'n kortpad wat parasitiese bestuurder kan oopmaak</string>\n    <string name=\"settings_shortcut_pinned_hint\">Shortcut pinned</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Die huidige versteklanseerder ondersteun nie penkortpaaie nie</string>\n    <string name=\"settings_enable_status_notification\">Statuskennisgewing</string>\n    <string name=\"settings_enable_status_notification_summary\">Wys \\'n kennisgewing wat parasitiese bestuurder kan oopmaak</string>\n    <string name=\"disable_status_notification_error\">Geen kortpad nie, kan nie kennisgewing deaktiveer nie</string>\n    <string name=\"settings_update_channel\">Dateer kanaal op</string>\n    <string name=\"update_channel_stable\">Stabiel</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nag bou</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API-oproepbeskerming</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokkeer dinamies gelaaide modulekode om Xposed API te gebruik, dit kan sommige modules breek, maar bevoordeel sekuriteit</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Lees my</string>\n    <string name=\"module_releases\">Vrystellings</string>\n    <string name=\"module_information\">Info</string>\n    <string name=\"module_information_homepage\">Tuisblad</string>\n    <string name=\"module_information_source_url\">Bronkode</string>\n    <string name=\"module_information_collaborators\">Medewerkers</string>\n    <string name=\"module_release_view_assets\">Bates</string>\n    <string name=\"menu_open_in_browser\">Maak oop in blaaier</string>\n    <string name=\"module_release_load_more\">Wys ouer weergawes</string>\n    <string name=\"module_release_no_more\">Geen vrystelling meer nie</string>\n    <string name=\"repo_load_failed\">Kon nie module repo laai nie: %s</string>\n    <string name=\"sort_upgradable_first\">Eers opgradeerbaar</string>\n    <string name=\"installed\">Geïnstalleer</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d aflaai</item>\n        <item quantity=\"other\">%d aflaaie</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rooi</string>\n    <string name=\"color_pink\">Pienk</string>\n    <string name=\"color_purple\">Pers</string>\n    <string name=\"color_deep_purple\">Diep pers</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blou</string>\n    <string name=\"color_light_blue\">Ligblou</string>\n    <string name=\"color_cyan\">Siaan</string>\n    <string name=\"color_teal\">Blauwgroen</string>\n    <string name=\"color_green\">Groen</string>\n    <string name=\"color_light_green\">Ligte groen</string>\n    <string name=\"color_lime\">Lemmetjie</string>\n    <string name=\"color_yellow\">Geel</string>\n    <string name=\"color_amber\">Amber</string>\n    <string name=\"color_orange\">Oranje</string>\n    <string name=\"color_deep_orange\">Diep oranje</string>\n    <string name=\"color_brown\">Bruin</string>\n    <string name=\"color_blue_grey\">Blou grys</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">ملخص</string>\n    <string name=\"Modules\">الوحدات</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"zero\">%d وحدة مفعلة</item>\n        <item quantity=\"one\">%d وحدة مفعلة</item>\n        <item quantity=\"two\">%d وحدة مفعلة</item>\n        <item quantity=\"few\">%d وحدات مفعلة</item>\n        <item quantity=\"many\">%d وحدة مفعلة</item>\n        <item quantity=\"other\">%d وحدة مفعلة</item>\n    </plurals>\n    <string name=\"Logs\">السجلات</string>\n    <string name=\"Settings\">الإعدادات</string>\n    <string name=\"feedback_or_suggestion\">ملاحظات أو اقتراحات</string>\n    <string name=\"About\">عنّ</string>\n    <string name=\"report_issue\">الإبلاغ عن مشكلة</string>\n    <string name=\"module_repo\">المُستودع</string>\n    <string name=\"module_repo_up_to_date\">جميع الوحدات محدثة</string>\n    <string name=\"module_repo_published_time\">تم نشرها في %s</string>\n    <string name=\"module_repo_updated_time\">تَم تحديثها في %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"zero\">%d وحدة قابلة للترقية</item>\n        <item quantity=\"one\">%d وحدة قابلة للترقية</item>\n        <item quantity=\"two\">%d وحدة قابلة للترقية</item>\n        <item quantity=\"few\">%d وحدات قابلة للترقية</item>\n        <item quantity=\"many\">%d وحدة قابلة للترقية</item>\n        <item quantity=\"other\">%d وحدة قابلة للترقية</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[عرض كود المصدر في %1$s<br/>انضم إلى قناتنا %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Mahmoud Abd El-Hamed (7TM)</string>\n    <string name=\"install\">تثبيت</string>\n    <string name=\"install_summary\">انقر لتثبيت LSPosed</string>\n    <string name=\"not_installed\">غير مثبت</string>\n    <string name=\"not_install_summary\">LSPosed غير مثبت</string>\n    <string name=\"activated\">مُفعّل</string>\n    <string name=\"partial_activated\">مفعل جزئياً</string>\n    <string name=\"selinux_policy_not_loaded_summary\">لم يتم تحميل SEPolicy بشكل صحيح</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[لن تعمل الوحدات النمطية التي تربط إطار عمل النظام.<br/>الرجاء الإبلاغ عن هذا إلى <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>ماجيسك</b></a> مطور النظام.]]></string>\n    <string name=\"system_inject_fail_summary\">فشل حقن إطار عمل النظام</string>\n    <string name=\"system_inject_fail\"><![CDATA[هذا نادر وقد يكون بسبب <b>Magisk</b> أو بعض وحدات Magisk منخفضة الجودة.<br/>الرجاء محاولة تعطيل وحدات Magisk خلاف Riru وLSPosed أو إرسال سجل كامل للمطورين.]]></string>\n    <string name=\"system_prop_incorrect_summary\">نظام prop غير صحيح</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[بعض خصائص النظام الضرورية تم حذفها أو تعديلها.<br/>قد تبطل الوحدات أحيانا.]]></string>\n    <string name=\"need_update\">تحتاج إلى التحديث</string>\n    <string name=\"please_update_summary\">يرجى تثبيت أحدث إصدار من LSPosed</string>\n    <string name=\"developer_warning_title\">نصائح لمطور الوحدة</string>\n    <string name=\"developer_warning_summary\">يرجى تعطيل تحسينات النشر على Android Studio، أو استخدام الأمر `gradlew installDebug` للتثبيت. وإلا فلن يتم تحديث ملف Apk للوحدة.</string>\n    <string name=\"info_api_version\">إصدار API</string>\n    <string name=\"info_framework_version\">إصدار إطار العمل</string>\n    <string name=\"info_manager_package_name\">Lاسم حزمة المديرo</string>\n    <string name=\"info_system_version\">إصدار النظام</string>\n    <string name=\"info_device\">الجهاز</string>\n    <string name=\"info_system_abi\">نظام ABI</string>\n    <string name=\"info_dex2oat_wrapper\">غلاف Dex المحسّن</string>\n    <string name=\"enabled\">مفعل</string>\n    <string name=\"not_enabled\">غير مفعل</string>\n    <string name=\"supported\">مدعوم</string>\n    <string name=\"unsupported\">غير متوافق</string>\n    <string name=\"android_version_unsatisfied\">نسخة أندرويد غير راضية</string>\n    <string name=\"crashed\">تعطّل</string>\n    <string name=\"mount_failed\">فشل التحميل</string>\n    <string name=\"selinux_permissive\">SELinux متساهل</string>\n    <string name=\"sepolicy_incorrect\">سياسة SELinux غير صحيحة</string>\n    <string name=\"update_lsposed\">تحديث LSPosed</string>\n    <string name=\"update_lsposed_msg\">تأكيد تحديث LSPosed؟ سيتم إعـادة تشغيل هذا الجهاز بعد اكتمال التحديث</string>\n    <string name=\"info_copied\">تم النسخ إلى الحافظة</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">مرحباً بك في LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">أنت تستخدم مدير الطفيليات، الذي يمكنه إنشاء اختصار أو لا يزال مفتوحا من الإشعارات.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">أنت تستخدم المدير الطفيلي، الذي يمكن فتحه من الإشعار.</string>\n    <string name=\"create_shortcut\">إنشاء إختصار</string>\n    <string name=\"never_show\">لا تظهر أبداً</string>\n    <string name=\"app_welcome\">مدير طفيلي موصي به</string>\n    <string name=\"app_welcome_summary\">يدعم LSPosed الآن تطهير النظام لتجنب الكشف، يمكنك فتح مدير الطفيليات من الإشعار. من المستحسن إلغاء تثبيت التطبيق الحالي.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">احفظ</string>\n    <string name=\"nav_item_logs_verbose\">سجلات مفصّلة</string>\n    <string name=\"nav_item_logs_module\">سجلات الوحدات</string>\n    <string name=\"logs_saving\">حفظ السجل ، يرجى الانتظار</string>\n    <string name=\"logs_saved\">تم حفظ السجلات</string>\n    <string name=\"logs_save_failed2\">فشل الحفظ:\\n%s</string>\n    <string name=\"menuClearLog\">مسح السجل الآن</string>\n    <string name=\"logs_cleared\">تم مسح السجل بنجاح.</string>\n    <string name=\"scroll_top\">التمرير لأعلى</string>\n    <string name=\"loading\">جارٍ التحميل…</string>\n    <string name=\"scroll_bottom\">التمرير لأسفل</string>\n    <string name=\"menuReload\">إعادة التحميل</string>\n    <string name=\"logs_clear_failed_2\">فشل مسح السجل</string>\n    <string name=\"menu_enable_word_wrap\">التفاف الكلمات</string>\n    <string name=\"enabled_verbose_log\">تم تفعيل السجل المفصّل</string>\n    <string name=\"disabled_verbose_log\">تم تعطيل السجل المفصّل</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(لم يتم تقديم وصف)</string>\n    <string name=\"warning_xposed_min_version\">هذه الوحدة تتطلب إصدار Xposed الأحدث (%d) لذا لا يمكن تفعيلها</string>\n    <string name=\"warning_target_version_higher\">تم تصميم هذه الوحدة لإصدار Xposed أحدث (%d) وبالتالي قد لا تعمل بعض الوظائف</string>\n    <string name=\"no_min_version_specified\">لا تحدد هذه الوحدة إصدار Xposed الذي تحتاجه.</string>\n    <string name=\"warning_min_version_too_low\">تم إنشاء هذه الوحدة لإصدار Xposed %1$d ، ولكن بسبب التغييرات غير المتوافقة في الإصدار %2$d، فقد تم تعطيلها</string>\n    <string name=\"warning_installed_on_external_storage\">لا يمكن تحميل هذه الوحدة لأنها مثبتة على بطاقة الذاكرة، يرجى نقلها إلى مساحة التخزين الداخلية</string>\n    <string name=\"module_uninstall\">إلغاء التثبيت</string>\n    <string name=\"module_settings\">إعدادات الوحدة</string>\n    <string name=\"view_in_repo\">عرض في المستودع</string>\n    <string name=\"module_uninstall_message\">هل تريد إلغاء تثبيت هذه الوحدة؟</string>\n    <string name=\"module_uninstalled\">تم إلغاء تثبيت %1$s</string>\n    <string name=\"module_uninstall_failed\">فشل إلغاء التثبيت</string>\n    <string name=\"add_module_to_user\">إضافة وحدة للمستخدم</string>\n    <string name=\"module_installed\">تم إضافة %1$s للمستخدم %2$s</string>\n    <string name=\"module_install_failed\">فشل إضافة الوحدة</string>\n    <string name=\"install_to_user\">تثبيت للمستخدم %s</string>\n    <string name=\"install_to_user_message\">هل ترغب في تثبيت %1$s للمستخدم %2$s؟ ينصح بالتثبيت يدوياً، قد يسبب إجبار التثبيت عبر LSPosed مشكلات.</string>\n    <string name=\"expand\">توسّع</string>\n    <string name=\"collapse\">انهيار</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">إعادة تحسين</string>\n    <string name=\"compile_speed_msg\">تحسين…</string>\n    <string name=\"compile_done\">اكتمل التحسين</string>\n    <string name=\"app_launch\">تشغيله</string>\n    <string name=\"compile_failed\">فشل التحسين: قيمة الإرجاع فارغة</string>\n    <string name=\"compile_failed_with_info\">فشل التحسين: </string>\n    <string name=\"sort_by_name\">أسم التطبيق</string>\n    <string name=\"sort_by_package_name\">أسم الحُزْمَة</string>\n    <string name=\"sort_by_install_time\">وقت التثبيت</string>\n    <string name=\"sort_by_update_time\">وقت التحديث</string>\n    <string name=\"sort_reverse\">عكسي</string>\n    <string name=\"menu_show_system_apps\">تطبيقات النظام</string>\n    <string name=\"menu_sort\">ترتيب</string>\n    <string name=\"enable_module\">تفعيل الوحدة</string>\n    <string name=\"no_scope_selected\">أنت لم تحدد أي تطبيق. المتابعة؟</string>\n    <string name=\"menu_show_games\">ألعاب</string>\n    <string name=\"menu_show_modules\">وحدات</string>\n    <string name=\"menu_show_denylist\">قائمة الرفض</string>\n    <string name=\"failed_to_save_scope_list\">فشل في حفظ قائمة النطاق</string>\n    <string name=\"app_version\">الإصدار: %1$s</string>\n    <string name=\"use_recommended\">مُوصى به</string>\n    <string name=\"no_scope_selected_has_recommended\">أنت لم تحدد أي تطبيق. تحديد التطبيقات الموصى بها؟</string>\n    <string name=\"use_recommended_message\">تحديد التطبيقات الموصى بها؟</string>\n    <string name=\"module_is_not_activated_yet\">وحدة Xposed لم يتم تفعيلها بعد</string>\n    <string name=\"requested_by_module\">مُوصى به</string>\n    <string name=\"update_available\">تحديث متاح: %1$s</string>\n    <string name=\"module_disabled_no_selection\">الوحدة %s تم تعطيلها لعدم تحديد أي تطبيق.</string>\n    <string name=\"android_framework\">إطار النظام</string>\n    <string name=\"menu_backup_and_restore\">نسخ احتياطي</string>\n    <string name=\"menu_backup\">نسخ احتياطي</string>\n    <string name=\"menu_restore\">استعادة</string>\n    <string name=\"force_stop\">إيقاف إجباري</string>\n    <string name=\"force_stop_dlg_title\">إيقاف إجباري؟</string>\n    <string name=\"force_stop_dlg_text\">إذا أغلقت التطبيق إجبارياً، قد يتصرف بشكل خاطئ.</string>\n    <string name=\"reboot_required\">إعادة التشغيل مطلوبة لتطبيق هذا التغيير</string>\n    <string name=\"reboot\">إعادة تشغيل</string>\n    <string name=\"menu_hide\">إخفاء</string>\n    <string name=\"deny_list\">%s على قائمة الرفض. قد لا يكون ساري المفعول.</string>\n    <string name=\"deny_list_info\">في قائمة الرفض</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">عرض في تطبيق آخر</string>\n    <string name=\"module_app_info\">معلومات التطبيق</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\n لا شيء هنا</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">إطار العمل</string>\n    <string name=\"settings_disable_verbose_log\">تعطيل السجلات المفصّلة</string>\n    <string name=\"settings_disable_verbose_log_summary\">الإبلاغ عن مشاكل طلب لتضمين السجلات المفصولة</string>\n    <string name=\"settings_enable_log_watchdog\">تمكين مراقبة السجل</string>\n    <string name=\"settings_enable_log_watchdog_summary\">يقوم مراقب السجل الخاص بـ LSPosed بتعديل خصائص النظام، والتي يمكن استغلالها للكشف عن LSPosed</string>\n    <string name=\"pure_black_dark_theme\">السمة السوداء المظلمة</string>\n    <string name=\"pure_black_dark_theme_summary\">استخدام السمة السوداء الخالصة إذا تم تمكين السمة المظلمة</string>\n    <string name=\"settings_group_theme\">السمة</string>\n    <string name=\"settings_backup_and_restore\">النسخ الاحتياطي والاستعادة</string>\n    <string name=\"settings_backup_summery\">نسخ احتياطي لقائمة الوحدات وقوائم النطاق.</string>\n    <string name=\"settings_restore_summery\">استعادة قائمة الوحدات وقوائم النطاق.</string>\n    <string name=\"settings_backup\">نسخ احتياطي</string>\n    <string name=\"settings_backup_failed2\">فشل في النسخ الاحتياطي:\\n%s</string>\n    <string name=\"enable_documentui\">الرجاء تمكين DocumentUI</string>\n    <string name=\"settings_restore\">استعادة</string>\n    <string name=\"settings_restore_failed2\">فشل في الاستعادة:\\n%s</string>\n    <string name=\"group_network\">شبكة</string>\n    <string name=\"dns_over_http\">DNS عبر HTTPS</string>\n    <string name=\"dns_over_http_summary\">حل بديل لتسمم DNS في بعض الدول</string>\n    <string name=\"theme_color\">لون السمة</string>\n    <string name=\"theme_color_system\">لون سمة النظام</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">إجبار التطبيقات على إظهار أيقونات المشغل</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">بعد أندرويد 10، لا يسمح للتطبيقات بإخفاء أيقونات المشغل. قم بإيقاف تشغيل التبديل لتعطيل مِيزة النظام هذه.</string>\n    <string name=\"settings_group_system\">نظام</string>\n    <string name=\"settings_language\">اللغة</string>\n    <string name=\"settings_translation_contributors\">المساهمون بالترجمة</string>\n    <string name=\"settings_translation\">المشاركة في الترجمة</string>\n    <string name=\"settings_translation_summary\">ساعدنا في ترجمة %s إلى لغتك</string>\n    <string name=\"settings_create_shortcut_summary\">إنشاء اختصار يمكنه فتح مدير الطفيليات</string>\n    <string name=\"settings_shortcut_pinned_hint\">تم تثبيت الاختصار</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">المشغل الافتراضي الحالي لا يدعم اختصارات الدبوس</string>\n    <string name=\"settings_enable_status_notification\">إشعارات الحالة</string>\n    <string name=\"settings_enable_status_notification_summary\">إظهار إشعار يمكنه فتح مدير الطفيليات</string>\n    <string name=\"disable_status_notification_error\">لا يوجد اختصار، لا يمكن تعطيل الإشعار</string>\n    <string name=\"settings_update_channel\">قناة التحديث</string>\n    <string name=\"update_channel_stable\">مستقر</string>\n    <string name=\"update_channel_bate\">تجريبي</string>\n    <string name=\"update_channel_nightly\">البناء الليلي</string>\n    <string name=\"settings_xposed_api_call_protection\">حماية استدعاء Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">حظر رمز الوحدة الذي يتم تحميله ديناميكيًا لاستخدام Xposed API ، قد يؤدي ذلك إلى كسر بعض الوحدات ولكنه يفيد الأمان</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">اقرأني</string>\n    <string name=\"module_releases\">إصدارات</string>\n    <string name=\"module_information\">معلومات</string>\n    <string name=\"module_information_homepage\">الصفحة الرئيسية</string>\n    <string name=\"module_information_source_url\">كود المصدر</string>\n    <string name=\"module_information_collaborators\">المتعاونين</string>\n    <string name=\"module_release_view_assets\">الأصول</string>\n    <string name=\"menu_open_in_browser\">فتح في المتصفح</string>\n    <string name=\"module_release_load_more\">إظهار الإصدارات الأقدم</string>\n    <string name=\"module_release_no_more\">لا مزيد من الإصدار</string>\n    <string name=\"repo_load_failed\">فشل تحميل مستودع الوحدة: %s</string>\n    <string name=\"sort_upgradable_first\">قابل للترقية أولاً</string>\n    <string name=\"installed\">المثبتة</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"zero\">%d التنزيلات</item>\n        <item quantity=\"one\">%d تنزيل</item>\n        <item quantity=\"two\">%d التنزيلات</item>\n        <item quantity=\"few\">%d التنزيلات</item>\n        <item quantity=\"many\">%d التنزيلات</item>\n        <item quantity=\"other\">%d التنزيلات</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">لون ساكورا</string>\n    <string name=\"color_red\">أحمر</string>\n    <string name=\"color_pink\">وردي</string>\n    <string name=\"color_purple\">بنفسجي</string>\n    <string name=\"color_deep_purple\">بنفسجي عميق</string>\n    <string name=\"color_indigo\">نيلي</string>\n    <string name=\"color_blue\">أزرق</string>\n    <string name=\"color_light_blue\">أزرق فاتح</string>\n    <string name=\"color_cyan\">سماوي</string>\n    <string name=\"color_teal\">أزرق مخضرّ</string>\n    <string name=\"color_green\">أخضر</string>\n    <string name=\"color_light_green\">أخضر فاتح</string>\n    <string name=\"color_lime\">ليموني</string>\n    <string name=\"color_yellow\">أصفر</string>\n    <string name=\"color_amber\">كهرماني</string>\n    <string name=\"color_orange\">برتقالي</string>\n    <string name=\"color_deep_orange\">برتقالي عميق</string>\n    <string name=\"color_brown\">بني</string>\n    <string name=\"color_blue_grey\">أزرق رمادي</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-bg/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">نظرة عامة</string>\n    <string name=\"Modules\">الوحدات</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d модулът е активиран</item>\n        <item quantity=\"other\">%d включени модули</item>\n    </plurals>\n    <string name=\"Logs\">Дневници</string>\n    <string name=\"Settings\">Настройки</string>\n    <string name=\"feedback_or_suggestion\">Обратна връзка или предложение</string>\n    <string name=\"About\">За нас</string>\n    <string name=\"report_issue\">Докладване на проблем</string>\n    <string name=\"module_repo\">Хранилище</string>\n    <string name=\"module_repo_up_to_date\">Всички модули са актуализирани</string>\n    <string name=\"module_repo_published_time\">Публикувано в %s</string>\n    <string name=\"module_repo_updated_time\">Акттуализиран на %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d модула има актуализация</item>\n        <item quantity=\"other\">%d модули с актуализации</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Преглед на изходния код в %1$s<br/>Присъединете се към нашия %2$s канал]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">невалидно</string>\n    <string name=\"install\">Инсталиране на</string>\n    <string name=\"install_summary\">Натиснете, за да инсталирате LSPosed</string>\n    <string name=\"not_installed\">Не е инсталиран</string>\n    <string name=\"not_install_summary\">LSPosed не е инсталиран</string>\n    <string name=\"activated\">Активиран</string>\n    <string name=\"partial_activated\">Частично активиран</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy не е заредена правилно</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Някои функции няма да работят.<br/>Моля, докладвайте за това на <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> разработчик.]]></string>\n    <string name=\"system_inject_fail_summary\">Неуспешно инжектиране на системната рамка</string>\n    <string name=\"system_inject_fail\"><![CDATA[Това се случва рядко и може да е причинено от <b>Magisk</b> или някои нискокачествени модули на Magisk.<br/>Моля, опитайте се да деактивирате модулите на Magisk, различни от Riru и LSPosed, или изпратете пълен журнал на разработчиците.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Неправилна стойност на системата</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Някои необходими системни свойства са изтрити или променени.<br/>Понякога модулите могат да се обезсилват.]]></string>\n    <string name=\"need_update\">Необходимо е да се актуализира</string>\n    <string name=\"please_update_summary\">Моля, инсталирайте най-новата версия на LSPosed</string>\n    <string name=\"info_api_version\">Версия на API</string>\n    <string name=\"info_framework_version\">Версия на рамката</string>\n    <string name=\"info_manager_package_name\">Име на пакета на мениджъра</string>\n    <string name=\"info_system_version\">Версия на системата</string>\n    <string name=\"info_device\">Устройство</string>\n    <string name=\"info_system_abi\">ABI на системата</string>\n    <string name=\"info_dex2oat_wrapper\">Обвивка на Dex Optimizer</string>\n    <string name=\"enabled\">Разрешено</string>\n    <string name=\"not_enabled\">Не е разрешено</string>\n    <string name=\"supported\">Поддържан</string>\n    <string name=\"unsupported\">Неподдържан</string>\n    <string name=\"android_version_unsatisfied\">Версията за Android е неудовлетворена</string>\n    <string name=\"crashed\">Счупен</string>\n    <string name=\"mount_failed\">Монтирането е неуспешно</string>\n    <string name=\"selinux_permissive\">SELinux е разрешаващ</string>\n    <string name=\"sepolicy_incorrect\">Политиката на SELinux е неправилна</string>\n    <string name=\"update_lsposed\">Актуализиране на LSPosed</string>\n    <string name=\"update_lsposed_msg\">Потвърждаване на актуализацията на LSPosed? Това устройство ще се рестартира след завършване на актуализацията</string>\n    <string name=\"info_copied\">Копиране в клипборда</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Добре дошли в LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Използвате паразитния мениджър, който може да създаде пряк път или все още да се отваря от известието.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Използвате паразитния мениджър, който може да се отвори от известие.</string>\n    <string name=\"create_shortcut\">Създаване на пряк път</string>\n    <string name=\"never_show\">Никога не показвайте</string>\n    <string name=\"app_welcome\">Препоръчва се паразитен мениджър</string>\n    <string name=\"app_welcome_summary\">LSPosed вече поддържа паразитиране на системата, за да се избегне откриването, можете да отворите мениджъра на паразити от известието. Препоръчва се да деинсталирате текущото приложение.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Запазете</string>\n    <string name=\"nav_item_logs_verbose\">Условни дневници</string>\n    <string name=\"nav_item_logs_module\">Дневници на модулите</string>\n    <string name=\"logs_saving\">Запазване на дневника, моля изчакайте</string>\n    <string name=\"logs_saved\">Запазени дневници</string>\n    <string name=\"logs_save_failed2\">Не успяхте да запазите:\\n%s</string>\n    <string name=\"menuClearLog\">Изчистване на дневника сега</string>\n    <string name=\"logs_cleared\">Дневникът е изчистен успешно.</string>\n    <string name=\"scroll_top\">Превъртете към началото</string>\n    <string name=\"loading\">Зареждане…</string>\n    <string name=\"scroll_bottom\">Превъртете към дъното</string>\n    <string name=\"menuReload\">Презареждане</string>\n    <string name=\"logs_clear_failed_2\">Неуспешно изчистване на дневника</string>\n    <string name=\"menu_enable_word_wrap\">Обвиване на думата</string>\n    <string name=\"enabled_verbose_log\">Разрешен е вербален дневник</string>\n    <string name=\"disabled_verbose_log\">Деактивиран е вербалният дневник</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(не е предоставено описание)</string>\n    <string name=\"warning_xposed_min_version\">Този модул изисква по-нова версия на Xposed (%d) и поради това не може да бъде активиран</string>\n    <string name=\"warning_target_version_higher\">Този модул е предназначен за по-нова версия на Xposed (%d) и поради това някои функционалности може да не работят</string>\n    <string name=\"no_min_version_specified\">Този модул не посочва версията на Xposed, която му е необходима.</string>\n    <string name=\"warning_min_version_too_low\">Този модул е създаден за Xposed версия %1$d, но поради несъвместими промени във версия %2$d, той е деактивиран</string>\n    <string name=\"warning_installed_on_external_storage\">Този модул не може да бъде зареден, защото е инсталиран на SD картата, моля, преместете го във вътрешната памет</string>\n    <string name=\"module_uninstall\">Деинсталиране на</string>\n    <string name=\"module_settings\">Настройки на модула</string>\n    <string name=\"view_in_repo\">Преглед в Repo</string>\n    <string name=\"module_uninstall_message\">Искате ли да деинсталирате този модул?</string>\n    <string name=\"module_uninstalled\">Деинсталиран %1$s</string>\n    <string name=\"module_uninstall_failed\">Деинсталирането е неуспешно</string>\n    <string name=\"add_module_to_user\">Добавяне на модул към потребителя</string>\n    <string name=\"module_installed\">Добавяне на %1$s към потребител %2$s</string>\n    <string name=\"module_install_failed\">Добавянето на модул е неуспешно</string>\n    <string name=\"install_to_user\">Инсталиране на потребител %s</string>\n    <string name=\"install_to_user_message\">Искате да инсталирате %1$s на потребител %2$s? Препоръчително е да се инсталира ръчно, принудителното инсталиране чрез LSPosed може да доведе до проблеми.</string>\n    <string name=\"expand\">разширяване на</string>\n    <string name=\"collapse\">срив</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Оптимизиране на</string>\n    <string name=\"compile_speed_msg\">Оптимизиране на…</string>\n    <string name=\"compile_done\">Оптимизацията е завършена</string>\n    <string name=\"app_launch\">Стартирайте го</string>\n    <string name=\"compile_failed\">Оптимизацията е неуспешна: върнатата стойност е празна</string>\n    <string name=\"compile_failed_with_info\">Оптимизацията е неуспешна: </string>\n    <string name=\"sort_by_name\">Име на приложението</string>\n    <string name=\"sort_by_package_name\">Име на пакета</string>\n    <string name=\"sort_by_install_time\">Време за инсталиране</string>\n    <string name=\"sort_by_update_time\">Време за актуализация</string>\n    <string name=\"sort_reverse\">Обратен</string>\n    <string name=\"menu_show_system_apps\">Системни приложения</string>\n    <string name=\"menu_sort\">Сортиране</string>\n    <string name=\"enable_module\">Включване на модула</string>\n    <string name=\"no_scope_selected\">Не сте избрали нито едно приложение. Продължете?</string>\n    <string name=\"menu_show_games\">Игри</string>\n    <string name=\"menu_show_modules\">Модули</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Неуспешно запазване на списъка с обхвата</string>\n    <string name=\"app_version\">Версия: %1$s</string>\n    <string name=\"use_recommended\">Препоръчителен</string>\n    <string name=\"no_scope_selected_has_recommended\">Не сте избрали нито едно приложение. Избрахте препоръчани приложения?</string>\n    <string name=\"use_recommended_message\">Изберете препоръчани приложения?</string>\n    <string name=\"module_is_not_activated_yet\">Модулът Xposed все още не е активиран</string>\n    <string name=\"requested_by_module\">Препоръчителен</string>\n    <string name=\"update_available\">Налична е актуализация: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Модулът %s е деактивиран, тъй като не е избрано приложение.</string>\n    <string name=\"android_framework\">Рамка на системата</string>\n    <string name=\"menu_backup_and_restore\">Резервно копие</string>\n    <string name=\"menu_backup\">Резервно копие</string>\n    <string name=\"menu_restore\">Възстановяване на</string>\n    <string name=\"force_stop\">Спиране на силата</string>\n    <string name=\"force_stop_dlg_title\">Насилствено спиране?</string>\n    <string name=\"force_stop_dlg_text\">Ако спрете приложение принудително, то може да се държи неправилно.</string>\n    <string name=\"reboot_required\">Необходимо е рестартиране, за да се приложи тази промяна.</string>\n    <string name=\"reboot\">Рестартиране на</string>\n    <string name=\"menu_hide\">Скрий</string>\n    <string name=\"deny_list\">%s е в denylist. Възможно е той да не влезе в сила.</string>\n    <string name=\"deny_list_info\">На denylist</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Преглед в друго приложение</string>\n    <string name=\"module_app_info\">Информация за приложението</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nНищо тук</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Рамка</string>\n    <string name=\"settings_disable_verbose_log\">Деактивиране на вербалните дневници</string>\n    <string name=\"settings_disable_verbose_log_summary\">Искане за включване на вербални дневници</string>\n    <string name=\"pure_black_dark_theme\">Черна тъмна тема</string>\n    <string name=\"pure_black_dark_theme_summary\">Използвайте чисто черната тема, ако е активирана тъмна тема</string>\n    <string name=\"settings_group_theme\">Тема</string>\n    <string name=\"settings_backup_and_restore\">Архивиране и възстановяване</string>\n    <string name=\"settings_backup_summery\">Списък с резервни копия на модули и списъци на обхвата.</string>\n    <string name=\"settings_restore_summery\">Възстановяване на списъците с модули и области.</string>\n    <string name=\"settings_backup\">Резервно копие</string>\n    <string name=\"settings_backup_failed2\">Неуспешно архивиране:\\n%s</string>\n    <string name=\"enable_documentui\">Моля, разрешете DocumentUI</string>\n    <string name=\"settings_restore\">Възстановяване на</string>\n    <string name=\"settings_restore_failed2\">Неуспешно възстановяване:\\n%s</string>\n    <string name=\"group_network\">Мрежа</string>\n    <string name=\"dns_over_http\">DNS през HTTPS</string>\n    <string name=\"dns_over_http_summary\">Заобикаляне на отравянето на DNS в някои държави</string>\n    <string name=\"theme_color\">Цвят на темата</string>\n    <string name=\"theme_color_system\">Цвят на темата на системата</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Принуждаване на приложенията да показват икони на стартирането</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">След Android 10 на приложенията не е позволено да скриват иконите си за стартиране. Изключете превключвателя, за да деактивирате тази системна функция.</string>\n    <string name=\"settings_group_system\">Система</string>\n    <string name=\"settings_language\">Език</string>\n    <string name=\"settings_translation_contributors\">Преводачи, които допринасят за превода</string>\n    <string name=\"settings_translation\">Участие в превод</string>\n    <string name=\"settings_translation_summary\">Помогнете ни да преведем %s на вашия език</string>\n    <string name=\"settings_create_shortcut_summary\">Създаване на пряк път, който може да отваря паразитен мениджър</string>\n    <string name=\"settings_shortcut_pinned_hint\">Кратък път, закачен</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Текущият стартер по подразбиране не поддържа преки пътища</string>\n    <string name=\"settings_enable_status_notification\">Известие за състоянието</string>\n    <string name=\"settings_enable_status_notification_summary\">Показване на известие, което може да отвори паразитен мениджър</string>\n    <string name=\"disable_status_notification_error\">Няма пряк път, не мога да деактивирам известието</string>\n    <string name=\"settings_update_channel\">Актуализиране на канала</string>\n    <string name=\"update_channel_stable\">Стабилен</string>\n    <string name=\"update_channel_bate\">Бета</string>\n    <string name=\"update_channel_nightly\">Нощно изграждане</string>\n    <string name=\"settings_xposed_api_call_protection\">Защита на повикванията на Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Блокиране на динамично зареждания код на модула, за да се използва Xposed API, това може да наруши някои модули, но е от полза за сигурността</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Освобождава</string>\n    <string name=\"module_information\">Информация</string>\n    <string name=\"module_information_homepage\">Начална страница</string>\n    <string name=\"module_information_source_url\">Изходен код</string>\n    <string name=\"module_information_collaborators\">Сътрудници</string>\n    <string name=\"module_release_view_assets\">Активи</string>\n    <string name=\"menu_open_in_browser\">Отваряне в браузъра</string>\n    <string name=\"module_release_load_more\">Показване на по-стари версии</string>\n    <string name=\"module_release_no_more\">Няма повече освобождаване</string>\n    <string name=\"repo_load_failed\">Неуспешно зареждане на модул repo: %s</string>\n    <string name=\"sort_upgradable_first\">Може да се надгражда първо</string>\n    <string name=\"installed\">Инсталиран</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d изтегляне</item>\n        <item quantity=\"other\">%d Изтегляния</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Сакура</string>\n    <string name=\"color_red\">Червено</string>\n    <string name=\"color_pink\">Розов</string>\n    <string name=\"color_purple\">Лилаво</string>\n    <string name=\"color_deep_purple\">Наситено лилаво</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Синьо</string>\n    <string name=\"color_light_blue\">Светлосиньо</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">Зелен</string>\n    <string name=\"color_light_green\">Светлозелено</string>\n    <string name=\"color_lime\">Lime</string>\n    <string name=\"color_yellow\">Жълт</string>\n    <string name=\"color_amber\">Амбър</string>\n    <string name=\"color_orange\">Orange</string>\n    <string name=\"color_deep_orange\">Наситено оранжево</string>\n    <string name=\"color_brown\">Кафяв</string>\n    <string name=\"color_blue_grey\">Синьо сиво</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-bn/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">সার সংক্ষেপ </string>\n    <string name=\"Modules\">মডিউল</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d মডিউল সক্ষম</item>\n        <item quantity=\"other\">%d মডিউল সক্রিয়</item>\n    </plurals>\n    <string name=\"Logs\">তথ্য সার-সংক্ষেপ </string>\n    <string name=\"Settings\">বিন্যাস</string>\n    <string name=\"feedback_or_suggestion\">প্রতিক্রিয়া বা পরামর্শ</string>\n    <string name=\"About\">সম্পর্কিত</string>\n    <string name=\"report_issue\">সমস্যা প্রতিবেদন </string>\n    <string name=\"module_repo\">ভান্ডার</string>\n    <string name=\"module_repo_up_to_date\">সব মডিউল আপ টু ডেট</string>\n    <string name=\"module_repo_published_time\">%sএ প্রকাশিত</string>\n    <string name=\"module_repo_updated_time\">%sএ আপডেট করা হয়েছে</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d মডিউল আপগ্রেডযোগ্য</item>\n        <item quantity=\"other\">%d মডিউল আপগ্রেডযোগ্য</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[%1$s<br/>এ সোর্স কোড দেখুন আমাদের %2$s চ্যানেলে যোগ দিন]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">bdtipsntricks</string>\n    <string name=\"install\">ইনস্টল করুন</string>\n    <string name=\"install_summary\">LSPosed ইনস্টল করতে আলতো চাপুন</string>\n    <string name=\"not_installed\">ইনস্টল করা না</string>\n    <string name=\"not_install_summary\">LSPosed ইনস্টল করা হয় না</string>\n    <string name=\"activated\">সক্রিয়</string>\n    <string name=\"partial_activated\">আংশিক সক্রিয়</string>\n    <string name=\"selinux_policy_not_loaded_summary\">এসইপলিসি সঠিকভাবে লোড করা হয় না</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[কিছু বৈশিষ্ট্য কাজ করবে না।<br/>অনুগ্রহ করে এটি <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> বিকাশকারীকে রিপোর্ট করুন।]]></string>\n    <string name=\"system_inject_fail_summary\">সিস্টেম ফ্রেমওয়ার্ক ইনজেকশন ব্যর্থ হয়েছে</string>\n    <string name=\"system_inject_fail\"><![CDATA[এটি বিরল এবং <b>Magisk</b> বা কিছু নিম্নমানের Magisk মডিউলের কারণে হতে পারে।<br/>অনুগ্রহ করে Riru এবং LSPosed ব্যতীত Magisk মডিউলগুলি নিষ্ক্রিয় করার চেষ্টা করুন বা বিকাশকারীদের কাছে সম্পূর্ণ লগ জমা দিন।]]></string>\n    <string name=\"system_prop_incorrect_summary\">সিস্টেম প্রপ ভুল</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[কিছু প্রয়োজনীয় সিস্টেম বৈশিষ্ট্য মুছে ফেলা বা সংশোধন করা হয়েছে.<br/>মডিউল মাঝে মাঝে অবৈধ হতে পারে।]]></string>\n    <string name=\"need_update\">আপডেট করতে হবে</string>\n    <string name=\"please_update_summary\">অনুগ্রহ করে LSPosed এর সর্বশেষ সংস্করণটি ইনস্টল করুন</string>\n    <string name=\"info_api_version\">API সংস্করণ</string>\n    <string name=\"info_framework_version\">ফ্রেমওয়ার্ক সংস্করণ</string>\n    <string name=\"info_manager_package_name\">ম্যানেজার প্যাকেজের নাম</string>\n    <string name=\"info_system_version\">সিস্টেম সংস্করণ</string>\n    <string name=\"info_device\">যন্ত্র</string>\n    <string name=\"info_system_abi\">সিস্টেম ABI</string>\n    <string name=\"info_dex2oat_wrapper\">ডেক্স অপ্টিমাইজার মোড়ক</string>\n    <string name=\"enabled\">সক্রিয়</string>\n    <string name=\"not_enabled\">সক্রিয় না</string>\n    <string name=\"supported\">সমর্থিত</string>\n    <string name=\"unsupported\">অসমর্থিত</string>\n    <string name=\"android_version_unsatisfied\">অ্যান্ড্রয়েড সংস্করণ অসন্তুষ্ট</string>\n    <string name=\"crashed\">বিধ্বস্ত</string>\n    <string name=\"mount_failed\">মাউন্ট ব্যর্থ হয়েছে</string>\n    <string name=\"selinux_permissive\">SELinux অনুমোদিত</string>\n    <string name=\"sepolicy_incorrect\">SELinux নীতি ভুল</string>\n    <string name=\"update_lsposed\">আপডেট LSPosed</string>\n    <string name=\"update_lsposed_msg\">LSPosed আপডেট করার জন্য নিশ্চিত? আপডেট সম্পূর্ণ হওয়ার পরে এই ডিভাইসটি রিবুট হবে</string>\n    <string name=\"info_copied\">ক্লিপবোর্ডে কপি করা হয়েছে</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPosed স্বাগতম</string>\n    <string name=\"parasitic_welcome_summary\">আপনি পরজীবী ম্যানেজার ব্যবহার করছেন, যা শর্টকাট তৈরি করতে পারে বা এখনও বিজ্ঞপ্তি থেকে খুলতে পারে।</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">আপনি পরজীবী ম্যানেজার ব্যবহার করছেন, যা বিজ্ঞপ্তি থেকে খুলতে পারে।</string>\n    <string name=\"create_shortcut\">শর্টকাট তৈরি করুন</string>\n    <string name=\"never_show\">কখনও দেখাবে না</string>\n    <string name=\"app_welcome\">পরজীবী ব্যবস্থাপক প্রস্তাবিত</string>\n    <string name=\"app_welcome_summary\">LSPosed এখন সনাক্তকরণ এড়াতে সিস্টেম প্যারাসাইটাইজেশন সমর্থন করে, আপনি বিজ্ঞপ্তি থেকে পরজীবী ম্যানেজার খুলতে পারেন। বর্তমান অ্যাপ্লিকেশনটি আনইনস্টল করার পরামর্শ দেওয়া হচ্ছে।</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">সংরক্ষণ</string>\n    <string name=\"nav_item_logs_verbose\">ভার্বোস লগ</string>\n    <string name=\"nav_item_logs_module\">মডিউল লগ</string>\n    <string name=\"logs_saving\">লগ সংরক্ষণ করা হচ্ছে, অনুগ্রহ করে অপেক্ষা করুন</string>\n    <string name=\"logs_saved\">লগ সংরক্ষিত</string>\n    <string name=\"logs_save_failed2\">সংরক্ষণ করতে ব্যর্থ হয়েছে:\\n%s</string>\n    <string name=\"menuClearLog\">এখন লগ সাফ করুন</string>\n    <string name=\"logs_cleared\">লগ সফলভাবে সাফ করা হয়েছে৷</string>\n    <string name=\"scroll_top\">উপরে যান</string>\n    <string name=\"loading\">লোড হচ্ছে…</string>\n    <string name=\"scroll_bottom\">নীচে স্ক্রোল করুন</string>\n    <string name=\"menuReload\">পুনরায় লোড করুন</string>\n    <string name=\"logs_clear_failed_2\">লগ সাফ করতে ব্যর্থ হয়েছে</string>\n    <string name=\"menu_enable_word_wrap\">শব্দ মোড়ানো</string>\n    <string name=\"enabled_verbose_log\">ভার্বোস লগ সক্ষম</string>\n    <string name=\"disabled_verbose_log\">ভার্বোস লগ নিষ্ক্রিয়</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(কোন বর্ণনা দেওয়া হয়নি)</string>\n    <string name=\"warning_xposed_min_version\">এই মডিউলটির একটি নতুন Xposed সংস্করণ প্রয়োজন (%d) এবং এইভাবে সক্রিয় করা যাবে না</string>\n    <string name=\"warning_target_version_higher\">এই মডিউলটি একটি নতুন Xposed সংস্করণ (%d) এর জন্য ডিজাইন করা হয়েছে এবং এইভাবে কিছু কার্যকারিতা কাজ নাও করতে পারে</string>\n    <string name=\"no_min_version_specified\">এই মডিউলটি তার প্রয়োজনীয় Xposed সংস্করণটি নির্দিষ্ট করে না।</string>\n    <string name=\"warning_min_version_too_low\">এই মডিউলটি Xposed সংস্করণ %1$d-এর জন্য তৈরি করা হয়েছিল, কিন্তু সংস্করণ %2$d-এ অসামঞ্জস্যপূর্ণ পরিবর্তনের কারণে, এটি নিষ্ক্রিয় করা হয়েছে।</string>\n    <string name=\"warning_installed_on_external_storage\">এই মডিউলটি লোড করা যাবে না কারণ এটি SD কার্ডে ইনস্টল করা আছে, দয়া করে এটিকে অভ্যন্তরীণ সঞ্চয়স্থানে নিয়ে যান৷</string>\n    <string name=\"module_uninstall\">আনইনস্টল করুন</string>\n    <string name=\"module_settings\">মডিউল সেটিংস</string>\n    <string name=\"view_in_repo\">রেপোতে দেখুন</string>\n    <string name=\"module_uninstall_message\">আপনি এই মডিউল আনইনস্টল করতে চান?</string>\n    <string name=\"module_uninstalled\">আনইনস্টল %1$s</string>\n    <string name=\"module_uninstall_failed\">আনইনস্টল করা যায়নি</string>\n    <string name=\"add_module_to_user\">ব্যবহারকারীর জন্য মডিউল যোগ করুন</string>\n    <string name=\"module_installed\">ব্যবহারকারী %2$sএ %1$s যোগ করা হয়েছে</string>\n    <string name=\"module_install_failed\">মডিউল যোগ করা ব্যর্থ হয়েছে৷</string>\n    <string name=\"install_to_user\">ব্যবহারকারী %sএ ইনস্টল করুন</string>\n    <string name=\"install_to_user_message\">ব্যবহারকারী %2$sথেকে %1$s ইনস্টল করতে চান? এটি ম্যানুয়ালি ইনস্টল করার সুপারিশ করা হয়, LSPosed এর মাধ্যমে জোর করে ইনস্টল করার ফলে সমস্যা হতে পারে।</string>\n    <string name=\"expand\">বিস্তৃত করা</string>\n    <string name=\"collapse\">পতন</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">পুনরায় অপ্টিমাইজ করুন</string>\n    <string name=\"compile_speed_msg\">অপ্টিমাইজ করা…</string>\n    <string name=\"compile_done\">অপ্টিমাইজেশান সম্পূর্ণ</string>\n    <string name=\"app_launch\">এটি চালু করুন</string>\n    <string name=\"compile_failed\">অপ্টিমাইজেশান ব্যর্থ হয়েছে: রিটার্ন মান খালি</string>\n    <string name=\"compile_failed_with_info\">অপ্টিমাইজেশান ব্যর্থ হয়েছে: </string>\n    <string name=\"sort_by_name\">আবেদনের নাম</string>\n    <string name=\"sort_by_package_name\">প্যাকেজের নাম</string>\n    <string name=\"sort_by_install_time\">ইন্সটল করার সময়</string>\n    <string name=\"sort_by_update_time\">আপডেটের সময়</string>\n    <string name=\"sort_reverse\">বিপরীত</string>\n    <string name=\"menu_show_system_apps\">সিস্টেম অ্যাপস</string>\n    <string name=\"menu_sort\">শ্রেণীবিভাজন</string>\n    <string name=\"enable_module\">মডিউল সক্ষম করুন</string>\n    <string name=\"no_scope_selected\">আপনি কোনো অ্যাপ নির্বাচন করেননি। চালিয়ে যান?</string>\n    <string name=\"menu_show_games\">গেমস</string>\n    <string name=\"menu_show_modules\">মডিউল</string>\n    <string name=\"menu_show_denylist\">অস্বীকারকারী</string>\n    <string name=\"failed_to_save_scope_list\">সুযোগ তালিকা সংরক্ষণ করতে ব্যর্থ হয়েছে</string>\n    <string name=\"app_version\">সংস্করণ: %1$s</string>\n    <string name=\"use_recommended\">প্রস্তাবিত</string>\n    <string name=\"no_scope_selected_has_recommended\">আপনি কোনো অ্যাপ নির্বাচন করেননি। প্রস্তাবিত অ্যাপ নির্বাচন করবেন?</string>\n    <string name=\"use_recommended_message\">প্রস্তাবিত অ্যাপ নির্বাচন করবেন?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed মডিউল এখনও সক্রিয় করা হয় নি</string>\n    <string name=\"requested_by_module\">প্রস্তাবিত</string>\n    <string name=\"update_available\">আপডেট উপলব্ধ: %1$s</string>\n    <string name=\"module_disabled_no_selection\">মডিউল %s অক্ষম করা হয়েছে যেহেতু কোনো অ্যাপ নির্বাচন করা হয়নি৷</string>\n    <string name=\"android_framework\">সিস্টেম ফ্রেমওয়ার্ক</string>\n    <string name=\"menu_backup_and_restore\">ব্যাকআপ</string>\n    <string name=\"menu_backup\">ব্যাকআপ</string>\n    <string name=\"menu_restore\">পুনরুদ্ধার করুন</string>\n    <string name=\"force_stop\">জোরপুর্বক থামা</string>\n    <string name=\"force_stop_dlg_title\">জোরপুর্বক থামা?</string>\n    <string name=\"force_stop_dlg_text\">আপনি যদি একটি অ্যাপকে জোর করে বন্ধ করেন, তাহলে সেটি খারাপ আচরণ করতে পারে।</string>\n    <string name=\"reboot_required\">এই পরিবর্তনটি প্রয়োগ করার জন্য রিবুট প্রয়োজন</string>\n    <string name=\"reboot\">রিবুট করুন</string>\n    <string name=\"menu_hide\">লুকান</string>\n    <string name=\"deny_list\">%s ডিনালিস্টে রয়েছে। এটি কার্যকর নাও হতে পারে।</string>\n    <string name=\"deny_list_info\">ডিনালিস্টে</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">অন্য অ্যাপে দেখুন</string>\n    <string name=\"module_app_info\">অ্যাপের তথ্য</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nএখানে কিছুই নেই</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">ফ্রেমওয়ার্ক</string>\n    <string name=\"settings_disable_verbose_log\">ভার্বোস লগ অক্ষম করুন</string>\n    <string name=\"settings_disable_verbose_log_summary\">ভার্বোস লগগুলি অন্তর্ভুক্ত করার জন্য সমস্যার প্রতিবেদন করুন</string>\n    <string name=\"pure_black_dark_theme\">কালো অন্ধকার থিম</string>\n    <string name=\"pure_black_dark_theme_summary\">অন্ধকার থিম সক্ষম থাকলে খাঁটি কালো থিম ব্যবহার করুন</string>\n    <string name=\"settings_group_theme\">থিম</string>\n    <string name=\"settings_backup_and_restore\">ব্যাকআপ এবং পুনঃস্থাপন</string>\n    <string name=\"settings_backup_summery\">ব্যাকআপ মডিউল তালিকা এবং সুযোগ তালিকা.</string>\n    <string name=\"settings_restore_summery\">মডিউল তালিকা এবং সুযোগ তালিকা পুনরুদ্ধার করুন।</string>\n    <string name=\"settings_backup\">ব্যাকআপ</string>\n    <string name=\"settings_backup_failed2\">ব্যাকআপ করতে ব্যর্থ হয়েছে:\\n%s</string>\n    <string name=\"enable_documentui\">অনুগ্রহ করে ডকুমেন্টইউআই সক্ষম করুন</string>\n    <string name=\"settings_restore\">পুনরুদ্ধার করুন</string>\n    <string name=\"settings_restore_failed2\">পুনরুদ্ধার করতে ব্যর্থ হয়েছে:\\n%s</string>\n    <string name=\"group_network\">অন্তর্জাল</string>\n    <string name=\"dns_over_http\">HTTPS এর উপর DNS</string>\n    <string name=\"dns_over_http_summary\">কিছু দেশে ডিএনএস বিষক্রিয়ার সমাধান</string>\n    <string name=\"theme_color\">থিম রঙ</string>\n    <string name=\"theme_color_system\">সিস্টেম থিম রঙ</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">অ্যাপ্লিকেশানগুলিকে লঞ্চার আইকনগুলি দেখাতে বাধ্য করুন৷</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">অ্যান্ড্রয়েড 10 এর পরে, অ্যাপগুলিকে তাদের লঞ্চার আইকনগুলি লুকানোর অনুমতি দেওয়া হয় না। এই সিস্টেম বৈশিষ্ট্যটি নিষ্ক্রিয় করতে টগলটি বন্ধ করুন৷</string>\n    <string name=\"settings_group_system\">পদ্ধতি</string>\n    <string name=\"settings_language\">ভাষা</string>\n    <string name=\"settings_translation_contributors\">অনুবাদ অবদানকারী</string>\n    <string name=\"settings_translation\">অনুবাদে অংশগ্রহণ করুন</string>\n    <string name=\"settings_translation_summary\">আপনার ভাষায় %s অনুবাদ করতে আমাদের সাহায্য করুন</string>\n    <string name=\"settings_create_shortcut_summary\">একটি শর্টকাট তৈরি করুন যা পরজীবী ম্যানেজার খুলতে পারে</string>\n    <string name=\"settings_shortcut_pinned_hint\">শর্টকাট পিন করা হয়েছে</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">বর্তমান ডিফল্ট লঞ্চার পিন শর্টকাট সমর্থন করে না</string>\n    <string name=\"settings_enable_status_notification\">স্থিতি বিজ্ঞপ্তি</string>\n    <string name=\"settings_enable_status_notification_summary\">পরজীবী ম্যানেজার খুলতে পারে এমন একটি বিজ্ঞপ্তি দেখান</string>\n    <string name=\"disable_status_notification_error\">কোন শর্টকাট, বিজ্ঞপ্তি নিষ্ক্রিয় করতে পারবেন না</string>\n    <string name=\"settings_update_channel\">চ্যানেল আপডেট করুন</string>\n    <string name=\"update_channel_stable\">স্থিতিশীল</string>\n    <string name=\"update_channel_bate\">বেটা</string>\n    <string name=\"update_channel_nightly\">রাতারাতি নির্মাণ</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API কল সুরক্ষা</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">এক্সপোজড এপিআই ব্যবহার করতে গতিশীলভাবে লোড করা মডিউল কোড ব্লক করুন, এটি কিছু মডিউল ভেঙে ফেলতে পারে তবে নিরাপত্তার সুবিধা পাবে</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">রিডমি</string>\n    <string name=\"module_releases\">মুক্তি দেয়</string>\n    <string name=\"module_information\">তথ্য</string>\n    <string name=\"module_information_homepage\">হোমপেজ</string>\n    <string name=\"module_information_source_url\">সোর্স কোড</string>\n    <string name=\"module_information_collaborators\">সহযোগীরা</string>\n    <string name=\"module_release_view_assets\">সম্পদ</string>\n    <string name=\"menu_open_in_browser\">ব্রাউজারে খোলা</string>\n    <string name=\"module_release_load_more\">পুরোনো সংস্করণ দেখান</string>\n    <string name=\"module_release_no_more\">আর মুক্তি নেই</string>\n    <string name=\"repo_load_failed\">মডিউল রেপো লোড করতে ব্যর্থ হয়েছে: %s</string>\n    <string name=\"sort_upgradable_first\">প্রথমে আপগ্রেডযোগ্য</string>\n    <string name=\"installed\">ইনস্টল করা হয়েছে</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d ডাউনলোড</item>\n        <item quantity=\"other\">%d ডাউনলোড</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">সাকুরা</string>\n    <string name=\"color_red\">লাল</string>\n    <string name=\"color_pink\">গোলাপী</string>\n    <string name=\"color_purple\">বেগুনি</string>\n    <string name=\"color_deep_purple\">গভীর বেগুনি</string>\n    <string name=\"color_indigo\">নীল</string>\n    <string name=\"color_blue\">নীল</string>\n    <string name=\"color_light_blue\">হালকা নীল</string>\n    <string name=\"color_cyan\">সায়ান</string>\n    <string name=\"color_teal\">টিল</string>\n    <string name=\"color_green\">সবুজ</string>\n    <string name=\"color_light_green\">হালকা সবুজ</string>\n    <string name=\"color_lime\">চুন</string>\n    <string name=\"color_yellow\">হলুদ</string>\n    <string name=\"color_amber\">অ্যাম্বার</string>\n    <string name=\"color_orange\">কমলা</string>\n    <string name=\"color_deep_orange\">গভীর কমলা</string>\n    <string name=\"color_brown\">বাদামী</string>\n    <string name=\"color_blue_grey\">নীল ধূসর</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Visió general</string>\n    <string name=\"Modules\">Mòduls</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d Mòdul activat</item>\n        <item quantity=\"other\">%d Mòduls activats</item>\n    </plurals>\n    <string name=\"Logs\">Logs</string>\n    <string name=\"Settings\">Configuració</string>\n    <string name=\"feedback_or_suggestion\">Comentari o suggeriment</string>\n    <string name=\"About\">Sobre</string>\n    <string name=\"report_issue\">Reportar problema</string>\n    <string name=\"module_repo\">Repositori</string>\n    <string name=\"module_repo_up_to_date\">Tots els mòduls actualitzats</string>\n    <string name=\"module_repo_published_time\">Published at %s</string>\n    <string name=\"module_repo_updated_time\">Actualitzat a les %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d mòdul actualitzable</item>\n        <item quantity=\"other\">%d mòduls actualitzables</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Veure codi font a %1$s<br/>Uneix-te al nostre %2$s canal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Frederic Blay,  Ghost Face, Yannick Kamin</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"install_summary\">Toca per instal·lar LSPosed</string>\n    <string name=\"not_installed\">No instalat</string>\n    <string name=\"not_install_summary\">LSPosed no està instal·lat</string>\n    <string name=\"activated\">Activat</string>\n    <string name=\"partial_activated\">Parcialment activat</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy no s\\'ha carregat correctament</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Algunes funcions no funcionaran.<br/>Informeu-ho al desenvolupador <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">La injecció del marc del sistema ha fallat</string>\n    <string name=\"system_inject_fail\"><![CDATA[Això és poc comú i podria ser causat per <b>Magisk</b> o algún Mòdul de baixa qualitat<br/>Si us plau, prova a desactivar els demés mòduls de magisk excepte Riru i LSPosed o envia un informe complet als desarrolladors.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Prop del sistema incorrecte</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[S\\'han suprimit o modificat algunes propietats necessàries del sistema.<br/>Els mòduls poden invalidar-se ocasionalment.]]></string>\n    <string name=\"need_update\">Cal actualitzar</string>\n    <string name=\"please_update_summary\">Instal·leu la darrera versió de LSPosed</string>\n    <string name=\"info_api_version\">Versió de l\\'API</string>\n    <string name=\"info_framework_version\">Versió del marc</string>\n    <string name=\"info_manager_package_name\">Nom del paquet del gestor</string>\n    <string name=\"info_system_version\">Versió del sistema</string>\n    <string name=\"info_device\">Dispositiu</string>\n    <string name=\"info_system_abi\">Sistema ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Embolcall de Dex Optimizer</string>\n    <string name=\"enabled\">Habilitat</string>\n    <string name=\"not_enabled\">No habilitat</string>\n    <string name=\"supported\">Admet</string>\n    <string name=\"unsupported\">Sense suport</string>\n    <string name=\"android_version_unsatisfied\">La versió d\\'Android no està satisfeta</string>\n    <string name=\"crashed\">Estavellat</string>\n    <string name=\"mount_failed\">El muntatge ha fallat</string>\n    <string name=\"selinux_permissive\">SELinux és permissiu</string>\n    <string name=\"sepolicy_incorrect\">La política de SELinux és incorrecta</string>\n    <string name=\"update_lsposed\">Actualització LSPosed</string>\n    <string name=\"update_lsposed_msg\">Confirmeu per actualitzar LSPosed? Aquest dispositiu es reiniciarà un cop finalitzada l\\'actualització</string>\n    <string name=\"info_copied\">S\\'ha copiat al porta-retalls</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Benvingut a LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Esteu utilitzant el gestor de paràsits, que pot crear dreceres o encara obrir-se des de la notificació.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Esteu utilitzant el gestor de paràsits, que es pot obrir des de la notificació.</string>\n    <string name=\"create_shortcut\">Crear accès directe</string>\n    <string name=\"never_show\">No mostris mai</string>\n    <string name=\"app_welcome\">Administrador de paràsits recomanat</string>\n    <string name=\"app_welcome_summary\">LSPosed ara admet la parasitització del sistema per evitar la detecció, podeu obrir el gestor de paràsits des de la notificació. Es recomana desinstal·lar l\\'aplicació actual.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Desa</string>\n    <string name=\"nav_item_logs_verbose\">Registres detallats</string>\n    <string name=\"nav_item_logs_module\">Registres de mòduls</string>\n    <string name=\"logs_saving\">S\\'està desant el registre, espereu</string>\n    <string name=\"logs_saved\">Registres guardats</string>\n    <string name=\"logs_save_failed2\">No s\\'ha pogut desar:\\n%s</string>\n    <string name=\"menuClearLog\">Esborra el registre ara</string>\n    <string name=\"logs_cleared\">El registre s\\'ha esborrat correctament.</string>\n    <string name=\"scroll_top\">Desplaceu-vos cap a dalt</string>\n    <string name=\"loading\">Carregant…</string>\n    <string name=\"scroll_bottom\">Desplaceu-vos cap avall</string>\n    <string name=\"menuReload\">Recarregar</string>\n    <string name=\"logs_clear_failed_2\">No s\\'ha pogut esborrar el registre</string>\n    <string name=\"menu_enable_word_wrap\">L\\'ajust de línia</string>\n    <string name=\"enabled_verbose_log\">Registre detallat activat</string>\n    <string name=\"disabled_verbose_log\">Registre detallat desactivat</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(no s\\'ofereix cap descripció)</string>\n    <string name=\"warning_xposed_min_version\">Aquest mòdul requereix una versió més nova de Xposed (%d) i per tant no es pot activar</string>\n    <string name=\"warning_target_version_higher\">Aquest mòdul està dissenyat per a una versió més nova de Xposed (%d) i per tant algunes funcionalitats poden no funcionar</string>\n    <string name=\"no_min_version_specified\">Aquest mòdul no especifica la versió de Xposed que necessita.</string>\n    <string name=\"warning_min_version_too_low\">Aquest mòdul es va crear per a Xposed versió %1$d, però a causa de canvis incompatibles a la versió %2$d, s\\'ha desactivat</string>\n    <string name=\"warning_installed_on_external_storage\">Aquest mòdul no es pot carregar perquè està instal·lat a la targeta SD, moveu-lo a l\\'emmagatzematge intern</string>\n    <string name=\"module_uninstall\">Desinstal·la</string>\n    <string name=\"module_settings\">Configuració del mòdul</string>\n    <string name=\"view_in_repo\">Veure a Repo</string>\n    <string name=\"module_uninstall_message\">Voleu desinstal·lar aquest mòdul?</string>\n    <string name=\"module_uninstalled\">Desinstal·lat %1$s</string>\n    <string name=\"module_uninstall_failed\">La desinstal·lació no s\\'ha realitzat correctament</string>\n    <string name=\"add_module_to_user\">Afegeix un mòdul a l\\'usuari</string>\n    <string name=\"module_installed\">S\\'ha afegit %1$s a l\\'usuari %2$s</string>\n    <string name=\"module_install_failed\">S\\'ha produït un error en afegir el mòdul</string>\n    <string name=\"install_to_user\">Instal·lar a l\\'usuari %s</string>\n    <string name=\"install_to_user_message\">Voleu instal·lar %1$s a l\\'usuari %2$s? Es recomana instal·lar manualment, forçar la instal·lació mitjançant LSPosed pot causar problemes.</string>\n    <string name=\"expand\">expandir</string>\n    <string name=\"collapse\">col·lapse</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Torna a optimitzar</string>\n    <string name=\"compile_speed_msg\">Optimització…</string>\n    <string name=\"compile_done\">Optimització completa</string>\n    <string name=\"app_launch\">Llança\\'l</string>\n    <string name=\"compile_failed\">L\\'optimització ha fallat: el valor de retorn és buit</string>\n    <string name=\"compile_failed_with_info\">L\\'optimització ha fallat: </string>\n    <string name=\"sort_by_name\">Nom de l\\'aplicació</string>\n    <string name=\"sort_by_package_name\">Nom del paquet</string>\n    <string name=\"sort_by_install_time\">Temps d\\'instal·lació</string>\n    <string name=\"sort_by_update_time\">Hora d\\'actualització</string>\n    <string name=\"sort_reverse\">Revés</string>\n    <string name=\"menu_show_system_apps\">Aplicacions del sistema</string>\n    <string name=\"menu_sort\">Classificació</string>\n    <string name=\"enable_module\">Activa el mòdul</string>\n    <string name=\"no_scope_selected\">No heu seleccionat cap aplicació. Continuar?</string>\n    <string name=\"menu_show_games\">Jocs</string>\n    <string name=\"menu_show_modules\">Mòduls</string>\n    <string name=\"menu_show_denylist\">Llista de denegació</string>\n    <string name=\"failed_to_save_scope_list\">No s\\'ha pogut desar la llista d\\'àmbits</string>\n    <string name=\"app_version\">Versió: %1$s</string>\n    <string name=\"use_recommended\">Recomanat</string>\n    <string name=\"no_scope_selected_has_recommended\">No heu seleccionat cap aplicació. Seleccioneu aplicacions recomanades?</string>\n    <string name=\"use_recommended_message\">Vols seleccionar aplicacions recomanades?</string>\n    <string name=\"module_is_not_activated_yet\">El mòdul Xposed encara no està activat</string>\n    <string name=\"requested_by_module\">Recomanat</string>\n    <string name=\"update_available\">Actualització disponible: %1$s</string>\n    <string name=\"module_disabled_no_selection\">El mòdul %s s\\'ha desactivat perquè no s\\'ha seleccionat cap aplicació.</string>\n    <string name=\"android_framework\">Marc del sistema</string>\n    <string name=\"menu_backup_and_restore\">Còpia de seguretat</string>\n    <string name=\"menu_backup\">Còpia de seguretat</string>\n    <string name=\"menu_restore\">Restaurar</string>\n    <string name=\"force_stop\">Parada forçada</string>\n    <string name=\"force_stop_dlg_title\">Parada forçada?</string>\n    <string name=\"force_stop_dlg_text\">Si forços l\\'aturada d\\'una aplicació, és possible que es comporti malament.</string>\n    <string name=\"reboot_required\">Cal reiniciar perquè s\\'apliqui aquest canvi</string>\n    <string name=\"reboot\">Reinicieu</string>\n    <string name=\"menu_hide\">Amaga</string>\n    <string name=\"deny_list\">%s és a la llista denegada. És possible que no tingui efecte.</string>\n    <string name=\"deny_list_info\">A la llista denegada</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Veure en una altra aplicació</string>\n    <string name=\"module_app_info\">Informació de l\\'aplicació</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nAquí no hi ha res</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Marc</string>\n    <string name=\"settings_disable_verbose_log\">Desactiva els registres detallats</string>\n    <string name=\"settings_disable_verbose_log_summary\">Sol·licitud d\\'informes de problemes per incloure registres detallats</string>\n    <string name=\"pure_black_dark_theme\">Tema negre fosc</string>\n    <string name=\"pure_black_dark_theme_summary\">Utilitzeu el tema negre pur si el tema fosc està habilitat</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Còpia de seguretat i restaurar</string>\n    <string name=\"settings_backup_summery\">Llista de mòduls de còpia de seguretat i llistes d\\'abast.</string>\n    <string name=\"settings_restore_summery\">Restaura la llista de mòduls i les llistes d\\'abast.</string>\n    <string name=\"settings_backup\">Còpia de seguretat</string>\n    <string name=\"settings_backup_failed2\">No s\\'ha pogut fer la còpia de seguretat:\\n%s</string>\n    <string name=\"enable_documentui\">Si us plau, activeu DocumentUI</string>\n    <string name=\"settings_restore\">Restaurar</string>\n    <string name=\"settings_restore_failed2\">No s\\'ha pogut restaurar:\\n%s</string>\n    <string name=\"group_network\">Xarxa</string>\n    <string name=\"dns_over_http\">DNS sobre HTTPS</string>\n    <string name=\"dns_over_http_summary\">Solució alternativa a l\\'enverinament per DNS en algunes nacions</string>\n    <string name=\"theme_color\">Color del tema</string>\n    <string name=\"theme_color_system\">Color del tema del sistema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Força les aplicacions a mostrar icones del llançador</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Després d\\'Android 10, les aplicacions no poden amagar les icones del llançador. Desactiveu el commutador per desactivar aquesta funció del sistema.</string>\n    <string name=\"settings_group_system\">Sistema</string>\n    <string name=\"settings_language\">Llenguatge</string>\n    <string name=\"settings_translation_contributors\">Col·laboradors de traducció</string>\n    <string name=\"settings_translation\">Participar en la traducció</string>\n    <string name=\"settings_translation_summary\">Ajuda\\'ns a traduir %s al teu idioma</string>\n    <string name=\"settings_create_shortcut_summary\">Creeu una drecera que pugui obrir el gestor de paràsits</string>\n    <string name=\"settings_shortcut_pinned_hint\">Drecera fixada</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">El llançador predeterminat actual no admet dreceres de pin</string>\n    <string name=\"settings_enable_status_notification\">Notificació d\\'estat</string>\n    <string name=\"settings_enable_status_notification_summary\">Mostra una notificació que pugui obrir el gestor de paràsits</string>\n    <string name=\"disable_status_notification_error\">Sense drecera, no es pot desactivar la notificació</string>\n    <string name=\"settings_update_channel\">Actualitza el canal</string>\n    <string name=\"update_channel_stable\">Estable</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Construcció nocturna</string>\n    <string name=\"settings_xposed_api_call_protection\">Protecció de trucades de l\\'API Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Bloqueja el codi del mòdul carregat dinàmicament per utilitzar l\\'API Xposed, això pot trencar alguns mòduls però beneficiar la seguretat</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Llegiu-me</string>\n    <string name=\"module_releases\">Alliberaments</string>\n    <string name=\"module_information\">Informació</string>\n    <string name=\"module_information_homepage\">Pàgina d\\'inici</string>\n    <string name=\"module_information_source_url\">Codi font</string>\n    <string name=\"module_information_collaborators\">Col·laboradors</string>\n    <string name=\"module_release_view_assets\">Actius</string>\n    <string name=\"menu_open_in_browser\">Oberta al navegador</string>\n    <string name=\"module_release_load_more\">Mostra les versions anteriors</string>\n    <string name=\"module_release_no_more\">No més llançament</string>\n    <string name=\"repo_load_failed\">No s\\'ha pogut carregar el dipòsit del mòdul: %s</string>\n    <string name=\"sort_upgradable_first\">Actualitzable primer</string>\n    <string name=\"installed\">Instal·lat</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d descàrrega</item>\n        <item quantity=\"other\">%d descàrregues</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Vermell</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Porpra</string>\n    <string name=\"color_deep_purple\">Lila fosc</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blau</string>\n    <string name=\"color_light_blue\">Blau clar</string>\n    <string name=\"color_cyan\">Cian</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">verd</string>\n    <string name=\"color_light_green\">Verd clar</string>\n    <string name=\"color_lime\">Lima</string>\n    <string name=\"color_yellow\">groc</string>\n    <string name=\"color_amber\">Ambre</string>\n    <string name=\"color_orange\">taronja</string>\n    <string name=\"color_deep_orange\">Taronja profund</string>\n    <string name=\"color_brown\">marró</string>\n    <string name=\"color_blue_grey\">Gris blau</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Přehled</string>\n    <string name=\"Modules\">Moduly</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul aktivován</item>\n        <item quantity=\"few\">%d modul povolen</item>\n        <item quantity=\"many\">%d Modul aktivován</item>\n        <item quantity=\"other\">%d Modul aktivován</item>\n    </plurals>\n    <string name=\"Logs\">Protokoly</string>\n    <string name=\"Settings\">Nastavení</string>\n    <string name=\"feedback_or_suggestion\">Zpětná vazba nebo návrh</string>\n    <string name=\"About\">O aplikaci</string>\n    <string name=\"report_issue\">Nahlásit problém</string>\n    <string name=\"module_repo\">Repozitář</string>\n    <string name=\"module_repo_up_to_date\">Všechny moduly jsou aktuální</string>\n    <string name=\"module_repo_published_time\">Publikováno v %s</string>\n    <string name=\"module_repo_updated_time\">Aktualizováno v %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul je možné aktualizovat</item>\n        <item quantity=\"few\">%d moduly je možné aktualizovat</item>\n        <item quantity=\"many\">%d modolů je možné aktualizovat</item>\n        <item quantity=\"other\">%d modulů je možné aktualizovat</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Zobrazit zdrojový kód na %1$s<br/>Připojte se k našemu kanálu %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">https://www.instagram.com/kasi33/</string>\n    <string name=\"install\">Instalovat</string>\n    <string name=\"install_summary\">Klepnutím nainstalujete LSPosed</string>\n    <string name=\"not_installed\">Není nainstalováno</string>\n    <string name=\"not_install_summary\">LSPosed není nainstalován</string>\n    <string name=\"activated\">Aktivováno</string>\n    <string name=\"partial_activated\">Částečně aktivováno</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy není správně načten</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Některé funkce nebudou fungovat.<br/>Nahlaste to prosím vývojáři <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Načtení System Framework se nezdařilo</string>\n    <string name=\"system_inject_fail\"><![CDATA[Toto je vzácné a může to být způsobeno <b>Magisk</b>em nebo některými málo kvalitními moduly Magisku.<br/>Zkuste vypnout moduly Magisk jiné než Riru a LSPosed nebo odešlete kompletní log vývojářům.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Nesprávné systémové prop</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Některé potřebné systémové vlastnosti byly odstraněny nebo změněny.<br/>Moduly mohou být někdy neplatné a tedy nefunkční.]]></string>\n    <string name=\"need_update\">Je třeba aktualizovat</string>\n    <string name=\"please_update_summary\">Nainstalujte si prosím nejnovější verzi LSPosed</string>\n    <string name=\"info_api_version\">Verze API</string>\n    <string name=\"info_framework_version\">Verze frameworku</string>\n    <string name=\"info_manager_package_name\">Název balíčku správce</string>\n    <string name=\"info_system_version\">Verze systému</string>\n    <string name=\"info_device\">Zařízení</string>\n    <string name=\"info_system_abi\">Systémové ABI (architektura)</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Povoleno</string>\n    <string name=\"not_enabled\">Není povoleno</string>\n    <string name=\"supported\">Podporováno</string>\n    <string name=\"unsupported\">Nepodporováno</string>\n    <string name=\"android_version_unsatisfied\">Verze Androidu není spokojena</string>\n    <string name=\"crashed\">Havaroval</string>\n    <string name=\"mount_failed\">Připojení se nezdařilo</string>\n    <string name=\"selinux_permissive\">SELinux je permisivní</string>\n    <string name=\"sepolicy_incorrect\">Zásady SELinuxu jsou nesprávné</string>\n    <string name=\"update_lsposed\">Aktualizovat LSPosed</string>\n    <string name=\"update_lsposed_msg\">Potvrdit aktualizaci LSPosed? Toto zařízení se restartuje po dokončení aktualizace</string>\n    <string name=\"info_copied\">Zkopírováno do schránky</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Vítejte v LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Používáte parazitního správce, který může vytvořit zástupce nebo se stále otevírat z oznámení.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Používáte parazitního správce, který se může otevřít z oznámení.</string>\n    <string name=\"create_shortcut\">Vytvořit zástupce</string>\n    <string name=\"never_show\">Nikdy nezobrazovat</string>\n    <string name=\"app_welcome\">Doporučený parazitický manažer</string>\n    <string name=\"app_welcome_summary\">LSPosed nyní podporuje parazitování systému. K zabránění detekce můžete správce parazitů otevřít z oznámení. Doporučuje se odinstalovat aktuální aplikaci.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Uložit</string>\n    <string name=\"nav_item_logs_verbose\">Podrobné protokoly</string>\n    <string name=\"nav_item_logs_module\">Protokoly modulů</string>\n    <string name=\"logs_saving\">Ukládání protokolu, čekejte prosím</string>\n    <string name=\"logs_saved\">Protokoly uloženy</string>\n    <string name=\"logs_save_failed2\">Nepodařilo se uložit:\\n%s</string>\n    <string name=\"menuClearLog\">Vymazat log</string>\n    <string name=\"logs_cleared\">Log byl úspěšně vymazán.</string>\n    <string name=\"scroll_top\">Přejít na začátek</string>\n    <string name=\"loading\">Načítání…</string>\n    <string name=\"scroll_bottom\">Přejít na začátek</string>\n    <string name=\"menuReload\">Znovu načíst</string>\n    <string name=\"logs_clear_failed_2\">Nepodařilo se vymazat protokol</string>\n    <string name=\"menu_enable_word_wrap\">Zalamování řádků</string>\n    <string name=\"enabled_verbose_log\">Podrobný záznam povolen</string>\n    <string name=\"disabled_verbose_log\">Podrobný záznam zakázán</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(žádný popis)</string>\n    <string name=\"warning_xposed_min_version\">Tento modul vyžaduje novější Xposed verzi (%d) a proto nemůže být aktivován</string>\n    <string name=\"warning_target_version_higher\">Tento modul je určen pro novější verzi Xposed (%d), a proto některé funkce nemusí fungovat</string>\n    <string name=\"no_min_version_specified\">Tento modul nespecifikuje potřebnou Xposed verzi.</string>\n    <string name=\"warning_min_version_too_low\">Tento modul byl vytvořen pro Xposed verzi %1$d, a tak z důvodu nekompatibilních změn ve verzi %2$dbyl zakázán</string>\n    <string name=\"warning_installed_on_external_storage\">Tento modul nelze načíst, protože je nainstalován na SD kartě, přesuňte jej na interní úložiště</string>\n    <string name=\"module_uninstall\">Odinstalovat</string>\n    <string name=\"module_settings\">Nastavení modulů</string>\n    <string name=\"view_in_repo\">Zobrazit v repozitáři</string>\n    <string name=\"module_uninstall_message\">Chcete odinstalovat tento modul?</string>\n    <string name=\"module_uninstalled\">%1$s odinstalován</string>\n    <string name=\"module_uninstall_failed\">Odinstalace nebyla úspěšná</string>\n    <string name=\"add_module_to_user\">Přidat modul k uživateli</string>\n    <string name=\"module_installed\">Modul %1$s přidán k uživateli %2$s</string>\n    <string name=\"module_install_failed\">Přidání modulu se nezdařilo</string>\n    <string name=\"install_to_user\">Instalovat uživateli %s</string>\n    <string name=\"install_to_user_message\">Chcete nainstalovat %1$s uživateli %2$s.? Je doporučeno instalovat ručně, vynucení instalace přes LSPosed může způsobit problémy.</string>\n    <string name=\"expand\">rozbalit</string>\n    <string name=\"collapse\">sbalit</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Znovu optimalizovat</string>\n    <string name=\"compile_speed_msg\">Optimalizace…</string>\n    <string name=\"compile_done\">Optimalizace dokončena</string>\n    <string name=\"app_launch\">Spustit</string>\n    <string name=\"compile_failed\">Optimalizace selhala: návratová hodnota je prázdná</string>\n    <string name=\"compile_failed_with_info\">Optimalizace selhala: </string>\n    <string name=\"sort_by_name\">Název aplikace</string>\n    <string name=\"sort_by_package_name\">Název balíčku</string>\n    <string name=\"sort_by_install_time\">Doba instalace</string>\n    <string name=\"sort_by_update_time\">Čas aktualizace</string>\n    <string name=\"sort_reverse\">Obrátit pořadí řazení </string>\n    <string name=\"menu_show_system_apps\">Systémové aplikace</string>\n    <string name=\"menu_sort\">Řazení</string>\n    <string name=\"enable_module\">Povolit modul</string>\n    <string name=\"no_scope_selected\">Nevybrali jste žádnou aplikaci. Pokračovat?</string>\n    <string name=\"menu_show_games\">Hry</string>\n    <string name=\"menu_show_modules\">Moduly</string>\n    <string name=\"menu_show_denylist\">Seznam zakázaných</string>\n    <string name=\"failed_to_save_scope_list\">Nepodařilo se uložit seznam</string>\n    <string name=\"app_version\">Verze: %1$s</string>\n    <string name=\"use_recommended\">Zvolit doporučené</string>\n    <string name=\"no_scope_selected_has_recommended\">Nevybrali jste žádnou aplikaci. Vybrat doporučené aplikace?</string>\n    <string name=\"use_recommended_message\">Vybrat doporučené aplikace?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modul ještě není aktivován</string>\n    <string name=\"requested_by_module\">Doporučené</string>\n    <string name=\"update_available\">Je k dispozici aktualizace: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s byl zakázán, protože nebyla vybrána žádná aplikace.</string>\n    <string name=\"android_framework\">Systémový Framework</string>\n    <string name=\"menu_backup_and_restore\">Zálohování</string>\n    <string name=\"menu_backup\">Zálohovat</string>\n    <string name=\"menu_restore\">Obnovení</string>\n    <string name=\"force_stop\">Vynutit zastavení</string>\n    <string name=\"force_stop_dlg_title\">Vynutit zastavení?</string>\n    <string name=\"force_stop_dlg_text\">Pokud vynutíte zastavení aplikace, může dojít k chybnému chování.</string>\n    <string name=\"reboot_required\">Pro aplikaci této změny je vyžadován restart</string>\n    <string name=\"reboot\">Restartovat</string>\n    <string name=\"menu_hide\">Skrýt</string>\n    <string name=\"deny_list\">%s je na seznamu zakázaných. Nemusí se projevit.</string>\n    <string name=\"deny_list_info\">Na seznamu zakázaných</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Zobrazit v jiné aplikaci</string>\n    <string name=\"module_app_info\">Informace o aplikaci</string>\n    <string name=\"list_empty\">&lt;unk&gt; \\\\\\\\_(&lt;unk&gt; )_\\/ &lt;unk&gt;\\nTady nic není</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Zakázat podrobné protokoly</string>\n    <string name=\"settings_disable_verbose_log_summary\">Nahlásit požadavek na problémy a zahrnout detailní záznamy</string>\n    <string name=\"pure_black_dark_theme\">Čistě černý motiv</string>\n    <string name=\"pure_black_dark_theme_summary\">Použít čistý černý motiv, pokud je tmavý motiv povolen</string>\n    <string name=\"settings_group_theme\">Vzhled</string>\n    <string name=\"settings_backup_and_restore\">Zálohování a obnovení</string>\n    <string name=\"settings_backup_summery\">Zálohovat seznam modulů a nastavení.</string>\n    <string name=\"settings_restore_summery\">Obnovit seznam modulů a nastavení.</string>\n    <string name=\"settings_backup\">Zálohování</string>\n    <string name=\"settings_backup_failed2\">Zálohování se nezdařilo:\\n%s</string>\n    <string name=\"enable_documentui\">Prosím povolte DocumentUI</string>\n    <string name=\"settings_restore\">Obnovení</string>\n    <string name=\"settings_restore_failed2\">Nepodařilo se obnovit:\\n%s</string>\n    <string name=\"group_network\">Síť</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">Řešení DNS oprav v některých zemích</string>\n    <string name=\"theme_color\">Barva motivu</string>\n    <string name=\"theme_color_system\">Barva systémového motivu</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Vynutit aplikace k zobrazení ikon spouštěče</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Od Androidu 10 není aplikacím povoleno skrývat ikony spouštěče. Vypněte přepínač pro vypnutí této systémové funkce.</string>\n    <string name=\"settings_group_system\">Systém</string>\n    <string name=\"settings_language\">Jazyk</string>\n    <string name=\"settings_translation_contributors\">Přispěvatelé překladu</string>\n    <string name=\"settings_translation\">Účast na překladu</string>\n    <string name=\"settings_translation_summary\">Pomozte nám přeložit %s do vašeho jazyka</string>\n    <string name=\"settings_create_shortcut_summary\">Vytvoření zástupce, který může otevřít parazitního správce</string>\n    <string name=\"settings_shortcut_pinned_hint\">Připnutí zástupci</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Současný výchozí spouštěč nepodporuje připnuté zkratky</string>\n    <string name=\"settings_enable_status_notification\">Oznámení o stavu</string>\n    <string name=\"settings_enable_status_notification_summary\">Zobrazení oznámení, které může otevřít parazitního správce</string>\n    <string name=\"disable_status_notification_error\">Žádný zástupce, nelze zakázat oznámení</string>\n    <string name=\"settings_update_channel\">Kanál aktualizace</string>\n    <string name=\"update_channel_stable\">Stabilní</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Noční sestavení</string>\n    <string name=\"settings_xposed_api_call_protection\">Ochrana volání rozhraní Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokování dynamicky načítaného kódu modulu pro použití rozhraní Xposed API, což může narušit některé moduly, ale prospěje bezpečnosti</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Přečti si mě</string>\n    <string name=\"module_releases\">Vydání</string>\n    <string name=\"module_information\">Informace</string>\n    <string name=\"module_information_homepage\">Domovská stránka</string>\n    <string name=\"module_information_source_url\">Zdrojový kód</string>\n    <string name=\"module_information_collaborators\">Spolupracovníci</string>\n    <string name=\"module_release_view_assets\">Assets</string>\n    <string name=\"menu_open_in_browser\">Otevřít v prohlížeči</string>\n    <string name=\"module_release_load_more\">Zobrazit starší verze</string>\n    <string name=\"module_release_no_more\">Žádné další vydání</string>\n    <string name=\"repo_load_failed\">Nepodařilo se načíst repozitář modulu: %s</string>\n    <string name=\"sort_upgradable_first\">Nejprve aktualizovatelný</string>\n    <string name=\"installed\">Instalováno</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d stažení</item>\n        <item quantity=\"few\">%d ke stažení</item>\n        <item quantity=\"many\">%d ke stažení</item>\n        <item quantity=\"other\">%d ke stažení</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Červená</string>\n    <string name=\"color_pink\">Růžová</string>\n    <string name=\"color_purple\">Fialová</string>\n    <string name=\"color_deep_purple\">Tmavě fialová</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Modrá</string>\n    <string name=\"color_light_blue\">Světle modrá</string>\n    <string name=\"color_cyan\">Azurová</string>\n    <string name=\"color_teal\">Modrozelená</string>\n    <string name=\"color_green\">Zelená</string>\n    <string name=\"color_light_green\">Světle zelená</string>\n    <string name=\"color_lime\">Limetková</string>\n    <string name=\"color_yellow\">Žlutá</string>\n    <string name=\"color_amber\">Jantarová</string>\n    <string name=\"color_orange\">Oranžová</string>\n    <string name=\"color_deep_orange\">Tmavě oranžová</string>\n    <string name=\"color_brown\">Hnědá</string>\n    <string name=\"color_blue_grey\">Modro-šedivá</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-da/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Oversigt</string>\n    <string name=\"Modules\">Moduler</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul aktiveret</item>\n        <item quantity=\"other\">%d moduler aktiveret</item>\n    </plurals>\n    <string name=\"Logs\">Logfiler</string>\n    <string name=\"Settings\">Indstillinger</string>\n    <string name=\"feedback_or_suggestion\">Feedback eller forslag</string>\n    <string name=\"About\">Om</string>\n    <string name=\"report_issue\">Anmeld problem</string>\n    <string name=\"module_repo\">Lagre</string>\n    <string name=\"module_repo_up_to_date\">Alle moduler opdateret</string>\n    <string name=\"module_repo_published_time\">Udgivet på %s</string>\n    <string name=\"module_repo_updated_time\">Opdateret på %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul opgraderbar</item>\n        <item quantity=\"other\">%d moduler opgraderbare</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Vis kildekode på %1$s<br/>Tilmeld dig vores %2$s kanal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Installér</string>\n    <string name=\"install_summary\">Tryk for at installere LSPosed</string>\n    <string name=\"not_installed\">Ikke installeret</string>\n    <string name=\"not_install_summary\">LSPosed er ikke installeret</string>\n    <string name=\"activated\">Aktiveret</string>\n    <string name=\"partial_activated\">Delvist aktiveret</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy er ikke indlæst korrekt</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Nogle funktioner vil ikke fungere.<br/>Du bedes rapportere dette til <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> udvikleren.]]></string>\n    <string name=\"system_inject_fail_summary\">System Framework injektion mislykkedes</string>\n    <string name=\"system_inject_fail\"><![CDATA[Dette er sjældent og kan være forårsaget af <b>Magisk</b> eller nogle lavkvalitets Magisk moduler.<br/>Prøv at deaktivere andre Magisk moduler end Riru og LSPosed eller indsende fuld log til udviklere.]]></string>\n    <string name=\"system_prop_incorrect_summary\">System prop forkert</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Nogle nødvendige systemegenskaber slettet eller ændret.<br/>Moduler kan ugyldiggøre lejlighedsvis.]]></string>\n    <string name=\"need_update\">Skal opdateres</string>\n    <string name=\"please_update_summary\">Installér venligst den seneste version af LSPosed</string>\n    <string name=\"info_api_version\">API version</string>\n    <string name=\"info_framework_version\">Rammer version</string>\n    <string name=\"info_manager_package_name\">Navn på manager-pakke</string>\n    <string name=\"info_system_version\">System version</string>\n    <string name=\"info_device\">Enhed</string>\n    <string name=\"info_system_abi\">System ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Aktiveret</string>\n    <string name=\"not_enabled\">Ikke aktiveret</string>\n    <string name=\"supported\">Understøttet</string>\n    <string name=\"unsupported\">Ikke understøttet</string>\n    <string name=\"android_version_unsatisfied\">Android-version utilfreds</string>\n    <string name=\"crashed\">Nedstyrtet</string>\n    <string name=\"mount_failed\">Montering mislykkedes</string>\n    <string name=\"selinux_permissive\">SELinux er tilladende</string>\n    <string name=\"sepolicy_incorrect\">SELinux-politik er forkert</string>\n    <string name=\"update_lsposed\">Opdater LSPosed</string>\n    <string name=\"update_lsposed_msg\">Bekræft opdatering af LSPosed? Denne enhed vil genstarte efter opdateringsfuldførelse</string>\n    <string name=\"info_copied\">Kopieret til udklipsholderen</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Velkommen til LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Du bruger den parasitære manager, som kan oprette genvej eller stadig åbne fra meddelelsen.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Du bruger den parasitære manager, som kan åbnes fra notifikationen.</string>\n    <string name=\"create_shortcut\">Opret genvej</string>\n    <string name=\"never_show\">Vis aldrig</string>\n    <string name=\"app_welcome\">Parasitic Manager Anbefalet</string>\n    <string name=\"app_welcome_summary\">LSPosed understøtter nu systemparasitering for at undgå registrering, du kan åbne parasitmanager fra meddelelsen. Det anbefales at afinstallere det aktuelle program.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Gem</string>\n    <string name=\"nav_item_logs_verbose\">Verbose Logs</string>\n    <string name=\"nav_item_logs_module\">Moduler Logs</string>\n    <string name=\"logs_saving\">Gemmer log, vent venligst</string>\n    <string name=\"logs_saved\">Gemte logfiler</string>\n    <string name=\"logs_save_failed2\">Mislykkedes at gemme:\\n%s</string>\n    <string name=\"menuClearLog\">Ryd log nu</string>\n    <string name=\"logs_cleared\">Loggen blev ryddet.</string>\n    <string name=\"scroll_top\">Rul til toppen</string>\n    <string name=\"loading\">Indlæser…</string>\n    <string name=\"scroll_bottom\">Rul til bunden</string>\n    <string name=\"menuReload\">Reload</string>\n    <string name=\"logs_clear_failed_2\">Kunne ikke rydde loggen</string>\n    <string name=\"menu_enable_word_wrap\">Tekstombrydning</string>\n    <string name=\"enabled_verbose_log\">Verbose log aktiveret</string>\n    <string name=\"disabled_verbose_log\">Verbose log deaktiveret</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(ingen beskrivelse angivet)</string>\n    <string name=\"warning_xposed_min_version\">Dette modul kræver en nyere Xposed version (%d) og kan derfor ikke aktiveres</string>\n    <string name=\"warning_target_version_higher\">Dette modul er designet til en nyere Xposed-version (%d), og derfor fungerer nogle funktioner muligvis ikke</string>\n    <string name=\"no_min_version_specified\">Dette modul angiver ikke den Xposed version det behøver.</string>\n    <string name=\"warning_min_version_too_low\">Dette modul blev oprettet til Xposed version %1$d, men på grund af inkompatible ændringer i version %2$d, er det blevet deaktiveret</string>\n    <string name=\"warning_installed_on_external_storage\">Dette modul kan ikke indlæses, da det er installeret på SD-kortet, flyt det til intern lagerplads</string>\n    <string name=\"module_uninstall\">Afinstaller</string>\n    <string name=\"module_settings\">Modul indstillinger</string>\n    <string name=\"view_in_repo\">Se i Repo</string>\n    <string name=\"module_uninstall_message\">Vil du afinstallere dette modul?</string>\n    <string name=\"module_uninstalled\">Afinstalleret %1$s</string>\n    <string name=\"module_uninstall_failed\">Afinstallation mislykkedes</string>\n    <string name=\"add_module_to_user\">Tilføj modul til bruger</string>\n    <string name=\"module_installed\">Tilføjede %1$s til bruger %2$s</string>\n    <string name=\"module_install_failed\">Tilføjelse af modul mislykkedes</string>\n    <string name=\"install_to_user\">Installér til bruger %s</string>\n    <string name=\"install_to_user_message\">Vil du installere %1$s til bruger %2$s? Det anbefales at installere manuelt, tvinger installation via LSPosed kan forårsage problemer.</string>\n    <string name=\"expand\">udvid</string>\n    <string name=\"collapse\">kollaps</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Genoptimér</string>\n    <string name=\"compile_speed_msg\">Optimerer…</string>\n    <string name=\"compile_done\">Optimering fuldført</string>\n    <string name=\"app_launch\">Start det</string>\n    <string name=\"compile_failed\">Optimering mislykkedes: returværdi er tom</string>\n    <string name=\"compile_failed_with_info\">Optimering mislykkedes: </string>\n    <string name=\"sort_by_name\">Applikations navn</string>\n    <string name=\"sort_by_package_name\">Pakke navn</string>\n    <string name=\"sort_by_install_time\">Installér tid</string>\n    <string name=\"sort_by_update_time\">Opdater tid</string>\n    <string name=\"sort_reverse\">Omvendt</string>\n    <string name=\"menu_show_system_apps\">System apps</string>\n    <string name=\"menu_sort\">Sortering</string>\n    <string name=\"enable_module\">Aktiver modul</string>\n    <string name=\"no_scope_selected\">Du valgte ikke nogen app. Fortsæt?</string>\n    <string name=\"menu_show_games\">Spil</string>\n    <string name=\"menu_show_modules\">Moduler</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Kunne ikke gemme scope-liste</string>\n    <string name=\"app_version\">Version: %1$s</string>\n    <string name=\"use_recommended\">Anbefalet</string>\n    <string name=\"no_scope_selected_has_recommended\">Du valgte ikke nogen app. Vælg anbefalede apps?</string>\n    <string name=\"use_recommended_message\">Vælg anbefalede apps?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modul er endnu ikke aktiveret</string>\n    <string name=\"requested_by_module\">Anbefalet</string>\n    <string name=\"update_available\">Opdatering tilgængelig: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s er blevet deaktiveret siden ingen app er valgt.</string>\n    <string name=\"android_framework\">System Framework</string>\n    <string name=\"menu_backup_and_restore\">Sikkerhedskopi</string>\n    <string name=\"menu_backup\">Sikkerhedskopi</string>\n    <string name=\"menu_restore\">Gendan</string>\n    <string name=\"force_stop\">Gennemtving stop</string>\n    <string name=\"force_stop_dlg_title\">Gennemtving stop?</string>\n    <string name=\"force_stop_dlg_text\">Hvis du tvinger til at stoppe en app, kan den virke forkert.</string>\n    <string name=\"reboot_required\">Genstart er påkrævet for at denne ændring kan anvendes</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Skjul</string>\n    <string name=\"deny_list\">%s er på denylist. Det kan ikke træde i kraft.</string>\n    <string name=\"deny_list_info\">På benægtelsesliste</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Se i anden app</string>\n    <string name=\"module_app_info\">Oplysninger om appen</string>\n    <string name=\"list_empty\">Spredning \\\\\\\\_(Ι)_\\/ Ι\\nIntet her</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Deaktivere udførlige logfiler</string>\n    <string name=\"settings_disable_verbose_log_summary\">Anmodning om at medtage verbose logs i rapporten om problemer</string>\n    <string name=\"pure_black_dark_theme\">Sort mørkt tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Brug det rene sorte tema, hvis mørkt tema er aktiveret</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Sikkerhedskopiér og gendan</string>\n    <string name=\"settings_backup_summery\">Backup modul liste og scope-lister.</string>\n    <string name=\"settings_restore_summery\">Gendan modulliste og scope-lister.</string>\n    <string name=\"settings_backup\">Sikkerhedskopi</string>\n    <string name=\"settings_backup_failed2\">Sikkerhedskopiering mislykkedes:\\n%s</string>\n    <string name=\"enable_documentui\">Aktiver venligst DocumentUI</string>\n    <string name=\"settings_restore\">Gendan</string>\n    <string name=\"settings_restore_failed2\">Kunne ikke gendanne:\\n%s</string>\n    <string name=\"group_network\">Netværk</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">Workaround DNS forgiftning i nogle nationer</string>\n    <string name=\"theme_color\">Tema farve</string>\n    <string name=\"theme_color_system\">Farve på systemtema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Tving apps til at vise launcher-ikoner</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Efter Android 10 må apps ikke skjule deres launcher-ikoner. Slå toggle fra for at deaktivere denne systemfunktion.</string>\n    <string name=\"settings_group_system\">System</string>\n    <string name=\"settings_language\">Sprog</string>\n    <string name=\"settings_translation_contributors\">Oversættelsesbidragsydere</string>\n    <string name=\"settings_translation\">Deltag i oversættelse</string>\n    <string name=\"settings_translation_summary\">Hjælp os med at oversætte %s til dit sprog</string>\n    <string name=\"settings_create_shortcut_summary\">Opret en genvej, der kan åbne parasitic manager</string>\n    <string name=\"settings_shortcut_pinned_hint\">Genvej fastgjort</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Den nuværende standardstarter understøtter ikke pin-genveje</string>\n    <string name=\"settings_enable_status_notification\">Meddelelse om status</string>\n    <string name=\"settings_enable_status_notification_summary\">Vis en meddelelse, der kan åbne parasitic manager</string>\n    <string name=\"disable_status_notification_error\">Ingen genvej, kan ikke deaktivere meddelelse</string>\n    <string name=\"settings_update_channel\">Opdater kanal</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly build</string>\n    <string name=\"settings_xposed_api_call_protection\">Beskyttelse af Xposed API-opkald</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Bloker dynamisk indlæst modulkode for at bruge Xposed API, hvilket kan ødelægge nogle moduler, men gavner sikkerheden</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Læs</string>\n    <string name=\"module_releases\">Udgivelser</string>\n    <string name=\"module_information\">Info</string>\n    <string name=\"module_information_homepage\">Hjemmeside</string>\n    <string name=\"module_information_source_url\">Kilde kode</string>\n    <string name=\"module_information_collaborators\">Samarbejdspartnere</string>\n    <string name=\"module_release_view_assets\">Aktiver</string>\n    <string name=\"menu_open_in_browser\">Åbn i browser</string>\n    <string name=\"module_release_load_more\">Vis ældre versioner</string>\n    <string name=\"module_release_no_more\">Ikke mere udgivelse</string>\n    <string name=\"repo_load_failed\">Kunne ikke indlæse modul repo: %s</string>\n    <string name=\"sort_upgradable_first\">Opgradérbar først</string>\n    <string name=\"installed\">Installeret</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d download</item>\n        <item quantity=\"other\">%d downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rød</string>\n    <string name=\"color_pink\">Lyserød</string>\n    <string name=\"color_purple\">Lilla</string>\n    <string name=\"color_deep_purple\">Dyb lilla</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blå</string>\n    <string name=\"color_light_blue\">Lyseblå</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Grønblåt</string>\n    <string name=\"color_green\">Grøn</string>\n    <string name=\"color_light_green\">Lysegrøn</string>\n    <string name=\"color_lime\">Limegrøn</string>\n    <string name=\"color_yellow\">Gul</string>\n    <string name=\"color_amber\">Ravgul</string>\n    <string name=\"color_orange\">Orange</string>\n    <string name=\"color_deep_orange\">Dyb orange</string>\n    <string name=\"color_brown\">Brun</string>\n    <string name=\"color_blue_grey\">Blå grå</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Übersicht</string>\n    <string name=\"Modules\">Module</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d Modul aktiviert</item>\n        <item quantity=\"other\">%d Module aktiviert</item>\n    </plurals>\n    <string name=\"Logs\">Protokolle</string>\n    <string name=\"Settings\">Einstellungen</string>\n    <string name=\"feedback_or_suggestion\">Feedback oder Vorschlag</string>\n    <string name=\"About\">Über</string>\n    <string name=\"report_issue\">Fehler melden</string>\n    <string name=\"module_repo\">Modularchiv</string>\n    <string name=\"module_repo_up_to_date\">Alle Module sind auf dem neusten Stand</string>\n    <string name=\"module_repo_published_time\">Veröffentlicht am %s</string>\n    <string name=\"module_repo_updated_time\">Aktualisiert am %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d Modul Update verfügbar</item>\n        <item quantity=\"other\">%d Modul Updates verfügbar</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Den Quellcode auf %1$s ansehen<br/>Folge unserem %2$s-Kanal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">mshinni80, \nJJ108</string>\n    <string name=\"install\">Installieren</string>\n    <string name=\"install_summary\">Tippen um LSPosed zu installieren</string>\n    <string name=\"not_installed\">Nicht installiert</string>\n    <string name=\"not_install_summary\">LSPosed ist nicht installiert</string>\n    <string name=\"activated\">Aktiviert</string>\n    <string name=\"partial_activated\">Teilweise aktiviert</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy wird nicht korrekt geladen</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Einige Funktionen werden nicht ordnungsgemäß funktionieren.<br/>Bitte melde es dem <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> Entwickler.]]></string>\n    <string name=\"system_inject_fail_summary\">System-Framework-Injektion fehlgeschlagen</string>\n    <string name=\"system_inject_fail\"><![CDATA[Dies ist selten und kann durch <b>Magisk</b> oder einige minderwertige Magisk Module verursacht werden.<br/>Bitte deaktiviere alle Magisk-Module bis auf Riru und LSPosed, oder sende eine vollständige Fehlermeldung an die Entwickler.]]></string>\n    <string name=\"system_prop_incorrect_summary\">System-Prop falsch</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Einige notwendige Systemeigenschaften wurden gelöscht oder geändert.<br/>Module können gelegentlich außer Kraft gesetzt werden.]]></string>\n    <string name=\"need_update\">Aktualisierung erforderlich</string>\n    <string name=\"please_update_summary\">Bitte installieren Sie die neueste Version von LSPosed</string>\n    <string name=\"developer_warning_title\">Tipps für Modulentwickler</string>\n    <string name=\"developer_warning_summary\">Bitte deaktiviere Deploy-Optimierungen in Android Studio oder benutze den `gradlew installDebug` Befehl zum Installieren. Andernfalls wird die Modul-Apk nicht aktualisiert.</string>\n    <string name=\"info_api_version\">API Version</string>\n    <string name=\"info_framework_version\">Framework Version</string>\n    <string name=\"info_manager_package_name\">Name des Managerpakets</string>\n    <string name=\"info_system_version\">System Version</string>\n    <string name=\"info_device\">Gerät</string>\n    <string name=\"info_system_abi\">System ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Aktiviert</string>\n    <string name=\"not_enabled\">Deaktiviert</string>\n    <string name=\"supported\">Unterstützt</string>\n    <string name=\"unsupported\">Nicht unterstützt</string>\n    <string name=\"android_version_unsatisfied\">Android-Version passt nicht</string>\n    <string name=\"crashed\">Abgestürzt</string>\n    <string name=\"mount_failed\">Mounten fehlgeschlagen</string>\n    <string name=\"selinux_permissive\">SELinux ist permissiv</string>\n    <string name=\"sepolicy_incorrect\">SELinux-Richtlinie ist falsch</string>\n    <string name=\"update_lsposed\">Bitte LSPosed aktualisieren</string>\n    <string name=\"update_lsposed_msg\">Soll LSPosed aktualisiert werden? Dieses Gerät wird nach dem Update neu gestartet</string>\n    <string name=\"info_copied\">In die Zwischenablage kopiert</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Willkommen bei LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Du verwendest den parasitären Manager, der eine Verknüpfung erstellen oder über eine Benachrichtigung noch geöffnet werden kann.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Du verwendest den parasitären Manager, der von der Benachrichtigung geöffnet werden kann.</string>\n    <string name=\"create_shortcut\">Verknüpfung erstellen</string>\n    <string name=\"never_show\">Niemals anzeigen</string>\n    <string name=\"app_welcome\">Parasitärer Manager empfohlen</string>\n    <string name=\"app_welcome_summary\">LSPosed unterstützt nun Systemparasitisierung, um eine Erkennung zu vermeiden. Du kannst den parasitären Manager über die Benachrichtigung öffnen. Es wird empfohlen, die aktuelle App zu deinstallieren.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Speichern</string>\n    <string name=\"nav_item_logs_verbose\">Ausführliche Protokolle</string>\n    <string name=\"nav_item_logs_module\">Modul-Protokolle</string>\n    <string name=\"logs_saving\">Protokoll wird gespeichert, bitte warten</string>\n    <string name=\"logs_saved\">Gespeicherte Protokolle</string>\n    <string name=\"logs_save_failed2\">Speichern fehlgeschlagen:\\n%s</string>\n    <string name=\"menuClearLog\">Protokoll jetzt löschen</string>\n    <string name=\"logs_cleared\">Protokoll erfolgreich gelöscht.</string>\n    <string name=\"scroll_top\">Hochscrollen</string>\n    <string name=\"loading\">Laden…</string>\n    <string name=\"scroll_bottom\">Runterscrollen</string>\n    <string name=\"menuReload\">Erneut laden</string>\n    <string name=\"logs_clear_failed_2\">Protokoll löschen fehlgeschlagen</string>\n    <string name=\"menu_enable_word_wrap\">Wortumbruch</string>\n    <string name=\"enabled_verbose_log\">Ausführliches Protokoll aktiviert</string>\n    <string name=\"disabled_verbose_log\">Ausführliches Protokoll deaktiviert</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(keine Beschreibung angegeben)</string>\n    <string name=\"warning_xposed_min_version\">Dieses Modul erfordert eine neuere Version von LSPosed (%d) und kann daher nicht aktiviert werden</string>\n    <string name=\"warning_target_version_higher\">Dieses Modul wurde für eine neuere Xposed-Version (%d) entwickelt und daher funktionieren einige Funktionen möglicherweise nicht</string>\n    <string name=\"no_min_version_specified\">Dieses Modul gibt nicht die benötigte LSPosed-Version an.</string>\n    <string name=\"warning_min_version_too_low\">Dieses Modul wurde für die LSPosed-Version %1$d erstellt, wurde jedoch aufgrund inkompatibler Änderungen in der Version %2$d deaktiviert</string>\n    <string name=\"warning_installed_on_external_storage\">Dieses Modul kann nicht geladen werden, da es auf der SD-Karte installiert ist, bitte in den internen Speicher verschieben</string>\n    <string name=\"module_uninstall\">Deinstallieren</string>\n    <string name=\"module_settings\">Moduleinstellungen</string>\n    <string name=\"view_in_repo\">In Repo anzeigen</string>\n    <string name=\"module_uninstall_message\">Möchtest du dieses Modul deinstallieren?</string>\n    <string name=\"module_uninstalled\">%1$s deinstalliert</string>\n    <string name=\"module_uninstall_failed\">Deinstallation fehlgeschlagen</string>\n    <string name=\"add_module_to_user\">Modul zum Benutzer hinzufügen</string>\n    <string name=\"module_installed\">%1$s zu Benutzer %2$s hinzugefügt</string>\n    <string name=\"module_install_failed\">Modul hinzufügen fehlgeschlagen</string>\n    <string name=\"install_to_user\">Auf Benutzer %s installieren</string>\n    <string name=\"install_to_user_message\">Möchtest du %1$s auf Benutzer %2$s installieren? Es wird empfohlen manuell zu installieren, das Erzwingen der Installation über LSPosed kann Probleme verursachen.</string>\n    <string name=\"expand\">ausklappen</string>\n    <string name=\"collapse\">einklappen</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Erneut optimieren</string>\n    <string name=\"compile_speed_msg\">Optimieren …</string>\n    <string name=\"compile_done\">Optimierung abgeschlossen.</string>\n    <string name=\"app_launch\">Starten</string>\n    <string name=\"compile_failed\">Optimierung fehlgeschlagen: Rückgabewert ist leer</string>\n    <string name=\"compile_failed_with_info\">Optimierung fehlgeschlagen: </string>\n    <string name=\"sort_by_name\">App-Name</string>\n    <string name=\"sort_by_package_name\">Paketname</string>\n    <string name=\"sort_by_install_time\">Installationszeit</string>\n    <string name=\"sort_by_update_time\">Aktualisierungszeit</string>\n    <string name=\"sort_reverse\">Umkehren</string>\n    <string name=\"menu_show_system_apps\">System-Apps</string>\n    <string name=\"menu_sort\">Sortieren</string>\n    <string name=\"enable_module\">Modul aktivieren</string>\n    <string name=\"no_scope_selected\">Du hast keine App ausgewählt. Weiter?</string>\n    <string name=\"menu_show_games\">Spiele</string>\n    <string name=\"menu_show_modules\">Module</string>\n    <string name=\"menu_show_denylist\">Verweigerungsliste</string>\n    <string name=\"failed_to_save_scope_list\">Scope-Liste speichern fehlgeschlagen</string>\n    <string name=\"app_version\">Version: %1$s</string>\n    <string name=\"menu_select\">Auswählen</string>\n    <string name=\"use_recommended\">Empfohlen</string>\n    <string name=\"no_scope_selected_has_recommended\">Du hast keine App ausgewählt. Empfohlene Apps auswählen?</string>\n    <string name=\"use_recommended_message\">Empfohlene Apps auswählen?</string>\n    <string name=\"menu_select_all\">Alle Auswählen</string>\n    <string name=\"menu_select_none\">Keine Auswahl</string>\n    <string name=\"menu_auto_include\">Automatisch einbinden</string>\n    <string name=\"module_is_not_activated_yet\">Das LSPosed-Modul wurde noch nicht aktiviert</string>\n    <string name=\"requested_by_module\">Empfohlen</string>\n    <string name=\"update_available\">Aktualisierung verfügbar: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s wurde deaktiviert, da keine App ausgewählt wurde.</string>\n    <string name=\"android_framework\">System-Framework</string>\n    <string name=\"menu_backup_and_restore\">Sichern</string>\n    <string name=\"menu_backup\">Sichern</string>\n    <string name=\"menu_restore\">Wiederherstellen</string>\n    <string name=\"force_stop\">Stopp erzwingen</string>\n    <string name=\"force_stop_dlg_title\">Stopp erzwingen?</string>\n    <string name=\"force_stop_dlg_text\">Wenn du einen App-Stopp erzwingst, können Probleme entstehen.</string>\n    <string name=\"reboot_required\">Neustart erforderlich, um diese Änderung zu übernehmen</string>\n    <string name=\"reboot\">Neustart</string>\n    <string name=\"menu_hide\">Ausblenden</string>\n    <string name=\"deny_list\">%s ist auf Verweigerungsliste. Dies könnte ohne Auswirkung bleiben.</string>\n    <string name=\"deny_list_info\">Auf Verweigerungsliste</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">In anderer App anzeigen</string>\n    <string name=\"module_app_info\">App-Information</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nNichts hier</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Ausführliche Protokolle deaktivieren</string>\n    <string name=\"settings_disable_verbose_log_summary\">Ausführliche Protokolle in Problemberichtsmeldungen einschließen</string>\n    <string name=\"settings_enable_log_watchdog\">Aktiviere Log Watchdog</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Der Log-Watchdog von LSPosed verändert Systemeigenschaften, die zum Aufspüren von LSPosed ausgenutzt werden können</string>\n    <string name=\"pure_black_dark_theme\">Dunkelschwarzes Thema</string>\n    <string name=\"pure_black_dark_theme_summary\">Schwarzes Thema verwenden, wenn dunkles Thema aktiviert ist</string>\n    <string name=\"settings_group_theme\">Design</string>\n    <string name=\"settings_backup_and_restore\">Sichern und Wiederherstellen</string>\n    <string name=\"settings_backup_summery\">Modul- und Scope-Listen sichern.</string>\n    <string name=\"settings_restore_summery\">Modul- und Scope-Listen wiederherstellen</string>\n    <string name=\"settings_backup\">Sichern</string>\n    <string name=\"settings_backup_failed2\">Sicherung fehlgeschlagen:\\n%s</string>\n    <string name=\"enable_documentui\">Bitte DocumentUI aktivieren</string>\n    <string name=\"settings_restore\">Wiederherstellen</string>\n    <string name=\"settings_restore_failed2\">Wiederherstellung fehlgeschlagen:\\n%s</string>\n    <string name=\"group_network\">Netzwerk</string>\n    <string name=\"dns_over_http\">DNS über HTTPS</string>\n    <string name=\"dns_over_http_summary\">Problemumgehung für DNS-Vergiftungen in einigen Ländern</string>\n    <string name=\"theme_color\">Designfarbe</string>\n    <string name=\"theme_color_system\">System Themenfarbe</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Apps erzwingen Launcher-Symbole anzuzeigen</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Ab Android 10 dürfen Apps ihre Launcher-Symbole nicht ausblenden. Schalte den Schalter aus, um diese System-Funktion zu deaktivieren.</string>\n    <string name=\"settings_group_system\">System</string>\n    <string name=\"settings_language\">Sprache</string>\n    <string name=\"settings_translation_contributors\">Übersetzer</string>\n    <string name=\"settings_translation\">Beim Übersetzen mitmachen</string>\n    <string name=\"settings_translation_summary\">Helfe uns, %s in deine Sprache zu übersetzen</string>\n    <string name=\"settings_create_shortcut_summary\">Eine Verknüpfung zum Öffnen des parasitären Managers erstellen</string>\n    <string name=\"settings_shortcut_pinned_hint\">Verknüpfung angeheftet</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Der aktuelle Standard-Launcher unterstützt keine Pin-Verknüpfungen</string>\n    <string name=\"settings_enable_status_notification\">Status-Benachrichtigung</string>\n    <string name=\"settings_enable_status_notification_summary\">Eine Benachrichtigung anzeigen, die den parasitären Manager öffnen kann</string>\n    <string name=\"disable_status_notification_error\">Keine Verknüpfung, Benachrichtigung kann nicht deaktiviert werden</string>\n    <string name=\"settings_update_channel\">Update-Kanal</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly Build</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API-Aufrufschutz</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Dynamisch geladenen Modulcode blockieren, um Xposed API zu verwenden. Einige Module werden nicht mehr funktionieren, aber die Sicherheit profitiert davon</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Liesmich</string>\n    <string name=\"module_releases\">Veröffentlichungen</string>\n    <string name=\"module_information\">Info</string>\n    <string name=\"module_information_homepage\">Webseite</string>\n    <string name=\"module_information_source_url\">Quellcode</string>\n    <string name=\"module_information_collaborators\">Mitarbeiter</string>\n    <string name=\"module_release_view_assets\">Ressourcen</string>\n    <string name=\"menu_open_in_browser\">Im Browser öffnen</string>\n    <string name=\"module_release_load_more\">Ältere Versionen anzeigen</string>\n    <string name=\"module_release_no_more\">Keine Veröffentlichung mehr</string>\n    <string name=\"repo_load_failed\">Modularchiv laden fehlgeschlagen: %s</string>\n    <string name=\"sort_upgradable_first\">Aktualisierbare zuerst</string>\n    <string name=\"installed\">Eingerichtet</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d Download</item>\n        <item quantity=\"other\">%d Downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rot</string>\n    <string name=\"color_pink\">Pink</string>\n    <string name=\"color_purple\">Lila</string>\n    <string name=\"color_deep_purple\">Dunkellila</string>\n    <string name=\"color_indigo\">Indigoblau</string>\n    <string name=\"color_blue\">Blau</string>\n    <string name=\"color_light_blue\">Hellblau</string>\n    <string name=\"color_cyan\">Türkis</string>\n    <string name=\"color_teal\">Blaugrün</string>\n    <string name=\"color_green\">Grün</string>\n    <string name=\"color_light_green\">Hellgrün</string>\n    <string name=\"color_lime\">Limette</string>\n    <string name=\"color_yellow\">Gelb</string>\n    <string name=\"color_amber\">Bernstein</string>\n    <string name=\"color_orange\">Orange</string>\n    <string name=\"color_deep_orange\">Dunkelorange</string>\n    <string name=\"color_brown\">Braun</string>\n    <string name=\"color_blue_grey\">Blaugrau</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Επισκόπηση</string>\n    <string name=\"Modules\">Πρόσθετα</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d  πρόσθετο ενεργοποιημένο</item>\n        <item quantity=\"other\">%d ενθέματα ενεργοποιήθηκαν</item>\n    </plurals>\n    <string name=\"Logs\">Αρχεία καταγραφής</string>\n    <string name=\"Settings\">Ρυθμίσεις</string>\n    <string name=\"feedback_or_suggestion\">Σχόλια ή πρόταση</string>\n    <string name=\"About\">Πληροφορίες</string>\n    <string name=\"report_issue\">Αναφορά προβλήματος</string>\n    <string name=\"module_repo\">Αποθετήριο</string>\n    <string name=\"module_repo_up_to_date\">Όλα τα πρόσθετα είναι ενημερωμένα</string>\n    <string name=\"module_repo_published_time\">Δημοσιεύθηκε στο %s</string>\n    <string name=\"module_repo_updated_time\">Ενημερώθηκε στο %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d ένθεμα αναβαθμίσιμο</item>\n        <item quantity=\"other\">%d πρόσθετα μπορούν να ενημερωθούν</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Δείτε τον πηγαίο κώδικα στο %1$s<br/>Εγγραφείτε στο %2$s κανάλι μας]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Aristeidis Alexopoulos</string>\n    <string name=\"install\">Εγκατάσταση</string>\n    <string name=\"install_summary\">Πατήστε για εγκατάσταση του LSPosed</string>\n    <string name=\"not_installed\">Μη εγκατεστημένο</string>\n    <string name=\"not_install_summary\">Το LSPosed δεν είναι εγκατεστημένο</string>\n    <string name=\"activated\">Ενεργοποιημένο</string>\n    <string name=\"partial_activated\">Μερικώς ενεργοποιημένο</string>\n    <string name=\"selinux_policy_not_loaded_summary\">Το SEPolicy δεν φορτώθηκε σωστά</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Ορισμένες λειτουργίες δεν θα λειτουργούν.<br/>Παρακαλούμε αναφέρετε το γεγονός αυτό στον προγραμματιστή <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> .]]></string>\n    <string name=\"system_inject_fail_summary\">Η έγχυση στο πλαίσιο συστήματος απέτυχε</string>\n    <string name=\"system_inject_fail\"><![CDATA[Αυτό είναι σπάνιο και μπορεί να προκληθεί από το <b>Magisk</b> ή από κάποια χαμηλής ποιότητας πρόσθετα τουMagisk.<br/>Παρακαλώ προσπαθήστε να απενεργοποιήσετε όλα τα πρόσθετα του Magisk εκτός από το Riru και το LSPosed ή να υποβάλετε το πλήρες αρχείο καταγραφής στους προγραμματιστές.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Λανθασμένο στήριγμα συστήματος</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Ορισμένες απαραίτητες ιδιότητες συστήματος διαγράφηκαν ή τροποποιήθηκαν.<br/>Τα πρόσθετα μπορεί να ακυρωθούν περιστασιακά.]]></string>\n    <string name=\"need_update\">Απαιτείται ενημέρωση</string>\n    <string name=\"please_update_summary\">Παρακαλώ εγκαταστήστε την τελευταία έκδοση του LSPosed</string>\n    <string name=\"info_api_version\">Έκδοση API</string>\n    <string name=\"info_framework_version\">Έκδοση πλαισίου</string>\n    <string name=\"info_manager_package_name\">Όνομα πακέτου διαχειριστή</string>\n    <string name=\"info_system_version\">Έκδοση συστήματος</string>\n    <string name=\"info_device\">Συσκευή</string>\n    <string name=\"info_system_abi\">Σύστημα ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Ενεργό</string>\n    <string name=\"not_enabled\">Μη ενεργό</string>\n    <string name=\"supported\">Υποστηριζόμενο</string>\n    <string name=\"unsupported\">Μη υποστηριζόμενο</string>\n    <string name=\"android_version_unsatisfied\">Ανικανοποίητη έκδοση Android</string>\n    <string name=\"crashed\">Συνετρίβη</string>\n    <string name=\"mount_failed\">Mount απέτυχε</string>\n    <string name=\"selinux_permissive\">Το SELinux είναι επιτρεπτικό</string>\n    <string name=\"sepolicy_incorrect\">Η πολιτική SELinux είναι λανθασμένη</string>\n    <string name=\"update_lsposed\">Ενημέρωση LSPosed</string>\n    <string name=\"update_lsposed_msg\">Επιβεβαίωση ενημέρωσης του LSPosed? Αυτή η συσκευή θα επανεκκινηθεί μετά την ολοκλήρωση της ενημέρωσης</string>\n    <string name=\"info_copied\">Αντιγραφή στο πρόχειρο</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Καλωσορίσατε στο LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Χρησιμοποιείτε τον παρασιτικό διαχειριστή, ο οποίος μπορεί να δημιουργήσει συντόμευση ή να παραμένει ανοικτός από την ειδοποίηση.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Χρησιμοποιείτε τον παρασιτικό διαχειριστή, ο οποίος μπορεί να δημιουργήσει συντόμευση ή να παραμένει ανοικτός από την ειδοποίηση.</string>\n    <string name=\"create_shortcut\">Δημιουργία συντόμευσης</string>\n    <string name=\"never_show\">Να μην εμφανίζεται ποτέ</string>\n    <string name=\"app_welcome\">Προτεινόμενος Παρασιτικός Διαχειριστής</string>\n    <string name=\"app_welcome_summary\">Το LSPosed υποστηρίζει τώρα την παρασιτοποίηση του συστήματος για να αποφύγετε την ανίχνευση, μπορείτε να ανοίξετε τον παρασιτικό διαχειριστή από την ειδοποίηση. Συνιστάται να απεγκαταστήσετε την τρέχουσα εφαρμογή.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Αποθήκευση</string>\n    <string name=\"nav_item_logs_verbose\">Λεπτομερείς Καταγραφές</string>\n    <string name=\"nav_item_logs_module\">Αρχείο Καταγραφής Πρόσθετων</string>\n    <string name=\"logs_saving\">Αποθήκευση αρχείου καταγραφής, παρακαλώ περιμένετε</string>\n    <string name=\"logs_saved\">Αποθηκευμένα αρχεία καταγραφής</string>\n    <string name=\"logs_save_failed2\">Αποτυχία αποθήκευσης:\\n%s</string>\n    <string name=\"menuClearLog\">Καθαρισμός αρχείου καταγραφής τώρα</string>\n    <string name=\"logs_cleared\">Το αρχείο καταγραφής εκκαθαρίστηκε επιτυχώς.</string>\n    <string name=\"scroll_top\">Κύλιση στην κορυφή</string>\n    <string name=\"loading\">Φόρτωση…</string>\n    <string name=\"scroll_bottom\">Κύλιση προς τα κάτω</string>\n    <string name=\"menuReload\">Φόρτωση ξανά</string>\n    <string name=\"logs_clear_failed_2\">Αποτυχία εκκαθάρισης του αρχείου καταγραφής</string>\n    <string name=\"menu_enable_word_wrap\">Αναδίπλωση Λέξεων</string>\n    <string name=\"enabled_verbose_log\">Ενεργοποίηση λεπτομερούς καταγραφής</string>\n    <string name=\"disabled_verbose_log\">Λεπτομερής καταγραφή απενεργοποιημένη</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(δεν παρέχεται περιγραφή)</string>\n    <string name=\"warning_xposed_min_version\">Αυτό το ένθεμα απαιτεί μια νεότερη έκδοση του Xposed (%d) και επομένως δεν μπορεί να ενεργοποιηθεί</string>\n    <string name=\"warning_target_version_higher\">Αυτή η ενότητα έχει σχεδιαστεί για μια νεότερη έκδοση Xposed (%d) και ως εκ τούτου ορισμένες λειτουργίες ενδέχεται να μην λειτουργούν.</string>\n    <string name=\"no_min_version_specified\">Αυτό το ένθεμα δεν καθορίζει την έκδοση Xposed που χρειάζεται.</string>\n    <string name=\"warning_min_version_too_low\">Αυτό το ένθεμα δημιουργήθηκε για την έκδοση Xposed %1$d, αλλά λόγω μη συμβατών αλλαγών στην έκδοση %2$d, έχει απενεργοποιηθεί</string>\n    <string name=\"warning_installed_on_external_storage\">Αυτό το πρόσθετο δεν μπορεί να φορτωθεί επειδή είναι εγκατεστημένο στην κάρτα SD, παρακαλώ μετακινήστε το στον εσωτερικό αποθηκευτικό χώρο</string>\n    <string name=\"module_uninstall\">Απεγκατάσταση</string>\n    <string name=\"module_settings\">Ρυθμίσεις πρόσθετου</string>\n    <string name=\"view_in_repo\">Προβολή στο Repo</string>\n    <string name=\"module_uninstall_message\">Θέλετε να απεγκαταστήσετε αυτό το πρόσθετο?</string>\n    <string name=\"module_uninstalled\">Απεγκαταστάθηκε %1$s</string>\n    <string name=\"module_uninstall_failed\">Απεγκατάσταση ανεπιτυχής</string>\n    <string name=\"add_module_to_user\">Προσθήκη module στο χρήστη</string>\n    <string name=\"module_installed\">Προστέθηκε %1$s στον χρήστη %2$s</string>\n    <string name=\"module_install_failed\">Η προσθήκη module απέτυχε</string>\n    <string name=\"install_to_user\">Εγκατάσταση σε χρήστη %s</string>\n    <string name=\"install_to_user_message\">Θέλετε να εγκαταστήσετε %1$s στο χρήστη %2$s? Συνιστάται η χειροκίνητη εγκατάσταση, αναγκάζοντας την εγκατάσταση μέσω LSPosed μπορεί να προκαλέσει προβλήματα.</string>\n    <string name=\"expand\">επέκταση</string>\n    <string name=\"collapse\">σύμπτυξη</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Επαναβελτιστοποίηση</string>\n    <string name=\"compile_speed_msg\">Βελτιστοποίηση…</string>\n    <string name=\"compile_done\">Η βελτιστοποίηση ολοκληρώθηκε</string>\n    <string name=\"app_launch\">Εκκίνηση</string>\n    <string name=\"compile_failed\">Η βελτιστοποίηση απέτυχε: η τιμή επιστροφής είναι κενή</string>\n    <string name=\"compile_failed_with_info\">Η βελτιστοποίηση απέτυχε: </string>\n    <string name=\"sort_by_name\">Όνομα εφαρμογής</string>\n    <string name=\"sort_by_package_name\">Όνομα πακέτου</string>\n    <string name=\"sort_by_install_time\">Χρόνος εγκατάστασης</string>\n    <string name=\"sort_by_update_time\">Χρόνος ενημέρωσης</string>\n    <string name=\"sort_reverse\">Αντίστροφη</string>\n    <string name=\"menu_show_system_apps\">Εφαρμογές συστήματος</string>\n    <string name=\"menu_sort\">Ταξινόμηση</string>\n    <string name=\"enable_module\">Ενεργοποίηση module</string>\n    <string name=\"no_scope_selected\">Δεν έχετε επιλέξει καμία εφαρμογή. Συνέχεια?</string>\n    <string name=\"menu_show_games\">Παιχνίδια</string>\n    <string name=\"menu_show_modules\">Πρόσθετα</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Αποτυχία αποθήκευσης της λίστας πεδίου</string>\n    <string name=\"app_version\">Έκδοση: %1$s</string>\n    <string name=\"use_recommended\">Προτεινόμενο</string>\n    <string name=\"no_scope_selected_has_recommended\">Δεν έχετε επιλέξει καμία εφαρμογή. Επιλέξτε τις προτεινόμενες εφαρμογές?</string>\n    <string name=\"use_recommended_message\">Επιλέξτε προτεινόμενες εφαρμογές?</string>\n    <string name=\"module_is_not_activated_yet\">Το Xposed πρόσθετο δεν έχει ενεργοποιηθεί ακόμα</string>\n    <string name=\"requested_by_module\">Προτεινόμενο</string>\n    <string name=\"update_available\">Διαθέσιμη ενημέρωση: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Το ένθεμα %s έχει απενεργοποιηθεί δεδομένου ότι δεν έχει επιλεγεί εφαρμογή.</string>\n    <string name=\"android_framework\">Πλαίσιο Συστήματος</string>\n    <string name=\"menu_backup_and_restore\">Αντίγραφα Ασφαλείας</string>\n    <string name=\"menu_backup\">Αντίγραφα Ασφαλείας</string>\n    <string name=\"menu_restore\">Επαναφορά</string>\n    <string name=\"force_stop\">Αναγκαστική διακοπή</string>\n    <string name=\"force_stop_dlg_title\">Αναγκαστική διακοπή?</string>\n    <string name=\"force_stop_dlg_text\">Αν επιβάλετε τη διακοπή μιας εφαρμογής, ενδέχεται να μην λειτουργήσει σωστά.</string>\n    <string name=\"reboot_required\">Απαιτείται επανεκκίνηση για να εφαρμοστεί αυτή η αλλαγή</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Απόκρυψη</string>\n    <string name=\"deny_list\">%s είναι σε denylist. Μπορεί να μην τεθεί σε ισχύ.</string>\n    <string name=\"deny_list_info\">Κατά την denylist</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Προβολή σε άλλη εφαρμογή</string>\n    <string name=\"module_app_info\">Πληροφορίες εφαρμογής</string>\n    <string name=\"list_empty\">◆ \\\\\\\\_(\\\")_\\/ \\\"\\nΤίποτα εδώ</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Απενεργοποιήστε αναλυτικά στοιχεία καταγραφής</string>\n    <string name=\"settings_disable_verbose_log_summary\">Αναφορά προβλημάτων ζητάει να συμπεριλαμβάνεται τα αναλυτικά στοιχεία καταγραφής</string>\n    <string name=\"pure_black_dark_theme\">Μαύρο σκούρο θέμα</string>\n    <string name=\"pure_black_dark_theme_summary\">Χρησιμοποιήστε το καθαρό μαύρο θέμα αν το σκούρο θέμα είναι ενεργοποιημένο</string>\n    <string name=\"settings_group_theme\">Θέμα</string>\n    <string name=\"settings_backup_and_restore\">Αντίγραφα ασφαλείας και επαναφορά</string>\n    <string name=\"settings_backup_summery\">Λίστα module αντιγράφων ασφαλείας και λίστες εμβέλειας.</string>\n    <string name=\"settings_restore_summery\">Επαναφορά λίστας ενθεμάτων και πεδίου εφαρμογής.</string>\n    <string name=\"settings_backup\">Αντίγραφα Ασφαλείας</string>\n    <string name=\"settings_backup_failed2\">Αποτυχία δημιουργίας αντιγράφων ασφαλείας:\\n%s</string>\n    <string name=\"enable_documentui\">Ενεργοποιήστε το DocumentUI</string>\n    <string name=\"settings_restore\">Επαναφορά</string>\n    <string name=\"settings_restore_failed2\">Αποτυχία επαναφοράς:\\n%s</string>\n    <string name=\"group_network\">Δίκτυο</string>\n    <string name=\"dns_over_http\">DNS μέσω HTTPS</string>\n    <string name=\"dns_over_http_summary\">Εργαστείτε γύρω από τη δηλητηρίαση DNS σε ορισμένες χώρες</string>\n    <string name=\"theme_color\">Χρώμα θέματος</string>\n    <string name=\"theme_color_system\">Χρώμα θέματος συστήματος</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Εξαναγκασμός των εφαρμογών να εμφανίζουν εικονίδια εκκίνησης</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Μετά το Android 10, οι εφαρμογές δεν επιτρέπεται να αποκρύψουν τα εικονίδια εκτοξευτή τους. Απενεργοποιήστε την εναλλαγή για να απενεργοποιήσετε αυτήν τη λειτουργία συστήματος.</string>\n    <string name=\"settings_group_system\">Σύστημα</string>\n    <string name=\"settings_language\">Γλώσσα</string>\n    <string name=\"settings_translation_contributors\">Συντελεστές μετάφρασης</string>\n    <string name=\"settings_translation\">Συμμετοχή στη μετάφραση</string>\n    <string name=\"settings_translation_summary\">Βοηθήστε μας να μεταφράσουμε το %s στη γλώσσα σας</string>\n    <string name=\"settings_create_shortcut_summary\">Δημιουργήστε μια συντόμευση που μπορεί να ανοίξει τον παρασιτικό διαχειριστή</string>\n    <string name=\"settings_shortcut_pinned_hint\">Συντόμευση καρφιτσωμένη</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Ο τρέχων προεπιλεγμένος εκτοξευτής δεν υποστηρίζει συντομεύσεις καρφίτσας</string>\n    <string name=\"settings_enable_status_notification\">Ειδοποίηση καταστάσεως</string>\n    <string name=\"settings_enable_status_notification_summary\">Εμφάνιση μιας ειδοποίησης που μπορεί να ανοίξει τον διαχειριστή παρασιτικής</string>\n    <string name=\"disable_status_notification_error\">Δεν υπάρχει συντόμευση, δεν είναι δυνατή η απενεργοποίηση της ειδοποίησης</string>\n    <string name=\"settings_update_channel\">Ενημέρωση καναλιού</string>\n    <string name=\"update_channel_stable\">Σταθερό</string>\n    <string name=\"update_channel_bate\">Βήτα</string>\n    <string name=\"update_channel_nightly\">Νυχτερινή κατασκευή</string>\n    <string name=\"settings_xposed_api_call_protection\">Προστασία κλήσεων Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Αποκλείστε τον δυναμικά φορτωμένο κώδικα μονάδας για να χρησιμοποιήσετε το Xposed API, αυτό μπορεί να σπάσει ορισμένες μονάδες αλλά να ωφελήσει την ασφάλεια</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Έτοιμο</string>\n    <string name=\"module_releases\">Εκδόσεις</string>\n    <string name=\"module_information\">Πληροφορίες</string>\n    <string name=\"module_information_homepage\">Αρχική</string>\n    <string name=\"module_information_source_url\">Πηγαίος κώδικας</string>\n    <string name=\"module_information_collaborators\">Συνεργάτες</string>\n    <string name=\"module_release_view_assets\">Ενεργητικό</string>\n    <string name=\"menu_open_in_browser\">Άνοιγμα σε πρόγραμμα περιήγησης</string>\n    <string name=\"module_release_load_more\">Εμφάνιση παλαιότερων εκδόσεων</string>\n    <string name=\"module_release_no_more\">Δεν υπάρχει πλέον έκδοση</string>\n    <string name=\"repo_load_failed\">Αποτυχία φόρτωσης πρόσθετου repo: %s</string>\n    <string name=\"sort_upgradable_first\">Αναβαθμίσιμο πρώτα</string>\n    <string name=\"installed\">Εγκατεστημένο</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d λήψη</item>\n        <item quantity=\"other\">%d downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Κόκκινο</string>\n    <string name=\"color_pink\">Ροζ</string>\n    <string name=\"color_purple\">Μωβ</string>\n    <string name=\"color_deep_purple\">Βαθύ μωβ</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Μπλε</string>\n    <string name=\"color_light_blue\">Ανοιχτό μπλε</string>\n    <string name=\"color_cyan\">Κυανό</string>\n    <string name=\"color_teal\">Τιρκουάζ</string>\n    <string name=\"color_green\">Πράσινο</string>\n    <string name=\"color_light_green\">Ανοιχτό πράσινο</string>\n    <string name=\"color_lime\">Άσβεστος</string>\n    <string name=\"color_yellow\">Κίτρινο</string>\n    <string name=\"color_amber\">Κεχριμπάρι</string>\n    <string name=\"color_orange\">Πορτοκαλί</string>\n    <string name=\"color_deep_orange\">Βαθύ πορτοκαλί</string>\n    <string name=\"color_brown\">Καφέ</string>\n    <string name=\"color_blue_grey\">Μπλε γκρι</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Resumen</string>\n    <string name=\"Modules\">Módulos</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d módulo activado</item>\n        <item quantity=\"other\">%d Módulos activados</item>\n    </plurals>\n    <string name=\"Logs\">Trozas</string>\n    <string name=\"Settings\">Configuración</string>\n    <string name=\"feedback_or_suggestion\">Comentarios o sugerencia</string>\n    <string name=\"About\">Acerca de</string>\n    <string name=\"report_issue\">Reportar problema</string>\n    <string name=\"module_repo\">Repositorio</string>\n    <string name=\"module_repo_up_to_date\">Todos los modulos actualizados</string>\n    <string name=\"module_repo_published_time\">Publicado el %s</string>\n    <string name=\"module_repo_updated_time\">Actualizado el %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d módulo actualizable</item>\n        <item quantity=\"other\">%d módulos repositorio actualizables</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Ve el código fuente en %1$s<br/>Únete a nuestro %2$s canal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">squaredDot</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"install_summary\">Pulsa para instalar LSPosed</string>\n    <string name=\"not_installed\">No instalado</string>\n    <string name=\"not_install_summary\">LSPosed no está instalado</string>\n    <string name=\"activated\">Activado</string>\n    <string name=\"partial_activated\">Parcialmente activado</string>\n    <string name=\"selinux_policy_not_loaded_summary\">Selinux policy no está cargado correctamente</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Algunas funciones no funcionarán.<br/>Informa de ello al desarrollador de <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> .]]></string>\n    <string name=\"system_inject_fail_summary\">System Framework injection failed</string>\n    <string name=\"system_inject_fail\"><![CDATA[This is rare and may be caused by <b>Magisk</b> or some low-quality Magisk modules.<br/>Please try to disable Magisk modules other than Riru and LSPosed or submit full log to developers.]]></string>\n    <string name=\"system_prop_incorrect_summary\">System prop incorrect</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Some necessary system properties deleted or modified.<br/>Modules may invalidate occasionally.]]></string>\n    <string name=\"need_update\">Need to update</string>\n    <string name=\"please_update_summary\">Por favor instale la última versión de LSPosed</string>\n    <string name=\"developer_warning_title\">Sugerencias para desarrolladores de módulos</string>\n    <string name=\"developer_warning_summary\">Por favor, desactiva las optimizaciones de implementación en Android Studio, o ejecuta el comando `gradlew installDebug` para instalar el módulo. De lo contrario, el apk del módulo no se actualizará.</string>\n    <string name=\"info_api_version\">Versión de la API</string>\n    <string name=\"info_framework_version\">Versión del framework</string>\n    <string name=\"info_manager_package_name\">Nombre del paquete de gestión</string>\n    <string name=\"info_system_version\">Versión del sistema</string>\n    <string name=\"info_device\">Dispositivo</string>\n    <string name=\"info_system_abi\">ABI del Sistema</string>\n    <string name=\"info_dex2oat_wrapper\">Envoltura del optimizador Dex</string>\n    <string name=\"enabled\">Activado</string>\n    <string name=\"not_enabled\">No habilitado</string>\n    <string name=\"supported\">Apoyado</string>\n    <string name=\"unsupported\">No soportado</string>\n    <string name=\"android_version_unsatisfied\">Versión Android insatisfecha</string>\n    <string name=\"crashed\">Se estrelló</string>\n    <string name=\"mount_failed\">El montaje falló</string>\n    <string name=\"selinux_permissive\">SELinux es permisivo</string>\n    <string name=\"sepolicy_incorrect\">La política de SELinux es incorrecta</string>\n    <string name=\"update_lsposed\">Actualizar LSPosed</string>\n    <string name=\"update_lsposed_msg\">¿Confirmar para actualizar LSPose? Este dispositivo se reiniciará después de completar la actualización</string>\n    <string name=\"info_copied\">Información copiada al portapapeles</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Bienvenido a LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Usted está utilizando el gestor de parásitos, que puede crear acceso directo o todavía abierta de notificación.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Estás usando el gestor de parásitos, que se puede abrir desde la notificación.</string>\n    <string name=\"create_shortcut\">Crear acceso directo</string>\n    <string name=\"never_show\">Nunca mostrar</string>\n    <string name=\"app_welcome\">Se recomienda Parasitic Manager</string>\n    <string name=\"app_welcome_summary\">LSPosed ahora soporta la parasitación del sistema para evitar la detección, puede abrir el gestor de parásitos desde la notificación. Se recomienda desinstalar la aplicación actual.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Guardar</string>\n    <string name=\"nav_item_logs_verbose\">Registros detallados</string>\n    <string name=\"nav_item_logs_module\">Logs de los módulos</string>\n    <string name=\"logs_saving\">Guardando registro, por favor espere</string>\n    <string name=\"logs_saved\">Registros guardados</string>\n    <string name=\"logs_save_failed2\">No se pudo guardar:\\n%s</string>\n    <string name=\"menuClearLog\">Limpiar los registros</string>\n    <string name=\"logs_cleared\">Registros limpiados satisfactoriamente.</string>\n    <string name=\"scroll_top\">Desplazar hasta el inicio</string>\n    <string name=\"loading\">Cargando…</string>\n    <string name=\"scroll_bottom\">Desplazar hasta el final</string>\n    <string name=\"menuReload\">Recargar</string>\n    <string name=\"logs_clear_failed_2\">Fallo al limpiar los registros</string>\n    <string name=\"menu_enable_word_wrap\">Ajuste de palabras</string>\n    <string name=\"enabled_verbose_log\">Registro detallado habilitado</string>\n    <string name=\"disabled_verbose_log\">Registro detallado desactivado</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(sin descripción)</string>\n    <string name=\"warning_xposed_min_version\">Este módulo requiere una versión más nueva de Xposed (%d), por lo que no puede ser activado</string>\n    <string name=\"warning_target_version_higher\">Este módulo está diseñado para una versión más reciente Xposed (%d) y por lo tanto algunas funcionalidades pueden no funcionar</string>\n    <string name=\"no_min_version_specified\">Este módulo no especifica la versión de Xposed que necesita.</string>\n    <string name=\"warning_min_version_too_low\">Este módulo fue creado para la versión de Xposed %1$d, pero, debido a cambios incompatibles en la versión %2$d, ha sido desactivado</string>\n    <string name=\"warning_installed_on_external_storage\">Este módulo no puede ser cargado porque está instalado en la tarjeta SD. Por favor, muévelo al almacenamiento interno</string>\n    <string name=\"module_uninstall\">Desinstalar</string>\n    <string name=\"module_settings\">Configuración del módulo</string>\n    <string name=\"view_in_repo\">Ver en el repositorio</string>\n    <string name=\"module_uninstall_message\">¿Quieres desinstalar este módulo?</string>\n    <string name=\"module_uninstalled\">Desinstalado %1$s</string>\n    <string name=\"module_uninstall_failed\">Fallo en la desinstalación</string>\n    <string name=\"add_module_to_user\">Añadir módulo al usuario</string>\n    <string name=\"module_installed\">%1$s instalado %2$s</string>\n    <string name=\"module_install_failed\">Fallo en la instalación</string>\n    <string name=\"install_to_user\">Instalar al usuario %s</string>\n    <string name=\"install_to_user_message\">¿Quieres instalar %1$s al usuario %2$s? Se recomienda que lo instales manualmente; forzar la instalación a través de LSPosed puede causar problemas.</string>\n    <string name=\"expand\">expandir</string>\n    <string name=\"collapse\">contraer</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Optimizar de nuevo</string>\n    <string name=\"compile_speed_msg\">Optimizando…</string>\n    <string name=\"compile_done\">Optimización completada.</string>\n    <string name=\"app_launch\">Abrir</string>\n    <string name=\"compile_failed\">La optimización falló o devolvió un valor vacío.</string>\n    <string name=\"compile_failed_with_info\">Fallo en la optimización: </string>\n    <string name=\"sort_by_name\">Filtrar por nombre de aplicación</string>\n    <string name=\"sort_by_package_name\">Filtrar por nombre de paquete</string>\n    <string name=\"sort_by_install_time\">Filtrar por fecha de instalación</string>\n    <string name=\"sort_by_update_time\">Filtrar por fecha de actualización</string>\n    <string name=\"sort_reverse\">Invertir</string>\n    <string name=\"menu_show_system_apps\">Aplicaciones del sistema</string>\n    <string name=\"menu_sort\">Filtrando</string>\n    <string name=\"enable_module\">Activar módulo</string>\n    <string name=\"no_scope_selected\">No seleccionaste ninguna aplicación. ¿Quieres continuar?</string>\n    <string name=\"menu_show_games\">Juegos</string>\n    <string name=\"menu_show_modules\">Módulos</string>\n    <string name=\"menu_show_denylist\">Lista de denegación</string>\n    <string name=\"failed_to_save_scope_list\">Fallo al guardar la lista de scopes</string>\n    <string name=\"app_version\">Versión: %1$s</string>\n    <string name=\"menu_select\">Seleccionar</string>\n    <string name=\"use_recommended\">Recomendado</string>\n    <string name=\"no_scope_selected_has_recommended\">No seleccionaste ninguna aplicación. ¿Quieres seleccionar las aplicaciones recomendadas?</string>\n    <string name=\"use_recommended_message\">¿Quieres seleccionar las aplicaciones recomendadas?</string>\n    <string name=\"menu_select_all\">Todo</string>\n    <string name=\"menu_select_none\">Ninguno</string>\n    <string name=\"menu_auto_include\">Auto-Incluir</string>\n    <string name=\"module_is_not_activated_yet\">El módulo Xposed no está activado aún</string>\n    <string name=\"requested_by_module\">Recomendado</string>\n    <string name=\"update_available\">Actualización disponible: %1$s</string>\n    <string name=\"module_disabled_no_selection\">El módulo %s ha sido desactivado ya que no se ha seleccionado ninguna aplicación.</string>\n    <string name=\"android_framework\">Framework del sistema</string>\n    <string name=\"menu_backup_and_restore\">Respaldo</string>\n    <string name=\"menu_backup\">Hacer un respaldo</string>\n    <string name=\"menu_restore\">Restaurar</string>\n    <string name=\"force_stop\">Forzar la detención</string>\n    <string name=\"force_stop_dlg_title\">¿Quieres forzar la detención?</string>\n    <string name=\"force_stop_dlg_text\">Si fuerzas la detención de una aplicación puede que esta se comporte de manera indefinida.</string>\n    <string name=\"reboot_required\">Necesitas reiniciar la aplicación para aplicar este cambio</string>\n    <string name=\"reboot\">Reiniciar</string>\n    <string name=\"menu_hide\">Ocultar</string>\n    <string name=\"deny_list\">%s está en la lista de negación. Puede no tener efecto.</string>\n    <string name=\"deny_list_info\">En lista de denegación</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Ver en otra aplicación</string>\n    <string name=\"module_app_info\">Información de la aplicación</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nNo hay nada por aquí</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Desactivar registros detallados</string>\n    <string name=\"settings_disable_verbose_log_summary\">Solicitud de inclusión de registros detallados en los informes de incidencias</string>\n    <string name=\"settings_enable_log_watchdog\">Habilitar el watchdog de registro</string>\n    <string name=\"settings_enable_log_watchdog_summary\">El watchdog de registro de LSPosed modifica propiedades del sistema, lo que podría usarse para detectar LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Tema negro oscuro</string>\n    <string name=\"pure_black_dark_theme_summary\">Usar el tema negro puro si el tema oscuro está activado</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Respaldo y restauración</string>\n    <string name=\"settings_backup_summery\">Hacer un respaldo de la lista de módulos y scopes.</string>\n    <string name=\"settings_restore_summery\">Hacer una restauración de la lista de módulos y scopes.</string>\n    <string name=\"settings_backup\">Hacer un respaldo</string>\n    <string name=\"settings_backup_failed2\">Error al realizar la copia de seguridad:\\n%s</string>\n    <string name=\"enable_documentui\">Por favor, activa DocumentUI</string>\n    <string name=\"settings_restore\">Restaurar</string>\n    <string name=\"settings_restore_failed2\">Error al restaurar:\\n%s</string>\n    <string name=\"group_network\">Red</string>\n    <string name=\"dns_over_http\">DNS sobre HTTPS</string>\n    <string name=\"dns_over_http_summary\">Solución alternativa al ataque de DNS en algunos países</string>\n    <string name=\"theme_color\">Color del tema</string>\n    <string name=\"theme_color_system\">Color de acentuación del sistema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Forzar a las aplicaciones a mostrar los íconos del ejecutable</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">En versiones posteriores a Android 10 no se permite a las aplicaciones (especialmente los módulos de Xposed) a ocultar el logo de su ejecutable. Desactiva la opción para desactivar esta característica.</string>\n    <string name=\"settings_group_system\">Sistema</string>\n    <string name=\"settings_language\">Idioma</string>\n    <string name=\"settings_translation_contributors\">Colaboradores de traducción</string>\n    <string name=\"settings_translation\">Participar en la traducción</string>\n    <string name=\"settings_translation_summary\">Ayúdanos a traducir %s a tu idioma</string>\n    <string name=\"settings_create_shortcut_summary\">Crear un acceso directo que pueda abrir el gestor de parásitos</string>\n    <string name=\"settings_shortcut_pinned_hint\">Acceso directo anclado</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">El actual lanzador por defecto no admite accesos directos a pines</string>\n    <string name=\"settings_enable_status_notification\">Notificación de estado</string>\n    <string name=\"settings_enable_status_notification_summary\">Mostrar una notificación que puede abrir el gestor de parásitos</string>\n    <string name=\"disable_status_notification_error\">No hay acceso directo, no se puede desactivar la notificación</string>\n    <string name=\"settings_update_channel\">Actualizar canal</string>\n    <string name=\"update_channel_stable\">Estable</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Construcción nocturna</string>\n    <string name=\"settings_xposed_api_call_protection\">Protección de llamadas a la API Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Bloquear el código del módulo cargado dinámicamente para utilizar Xposed API, esto puede romper algunos módulos, pero beneficiar a la seguridad</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Léeme</string>\n    <string name=\"module_releases\">Versiones</string>\n    <string name=\"module_information\">Información</string>\n    <string name=\"module_information_homepage\">Página principal</string>\n    <string name=\"module_information_source_url\">Código fuente</string>\n    <string name=\"module_information_collaborators\">Colaboradores</string>\n    <string name=\"module_release_view_assets\">Archivos</string>\n    <string name=\"menu_open_in_browser\">Abrir en el navegador</string>\n    <string name=\"module_release_load_more\">Mostrar versiones anteriores</string>\n    <string name=\"module_release_no_more\">No hay más versiones</string>\n    <string name=\"repo_load_failed\">Fallo al cargar el módulo de repositorio: %s</string>\n    <string name=\"sort_upgradable_first\">Actualizables</string>\n    <string name=\"installed\">Instalado</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d descargar</item>\n        <item quantity=\"other\">%d descargas</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rojo</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Morado</string>\n    <string name=\"color_deep_purple\">Morado profundo</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Azul</string>\n    <string name=\"color_light_blue\">Azul claro</string>\n    <string name=\"color_cyan\">Cian</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">Verde</string>\n    <string name=\"color_light_green\">Verde claro</string>\n    <string name=\"color_lime\">Lima</string>\n    <string name=\"color_yellow\">Amarillo</string>\n    <string name=\"color_amber\">Ámbar</string>\n    <string name=\"color_orange\">Naranja</string>\n    <string name=\"color_deep_orange\">Naranja oscuro</string>\n    <string name=\"color_brown\">Marrón</string>\n    <string name=\"color_blue_grey\">Gris azul</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-et/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Kodu</string>\n    <string name=\"Modules\">Moodulid</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d moodul on lubatud</item>\n        <item quantity=\"other\">%d moodulit on lubatud</item>\n    </plurals>\n    <string name=\"Logs\">Logid</string>\n    <string name=\"Settings\">Seaded</string>\n    <string name=\"feedback_or_suggestion\">Tagasiside või ettepanek</string>\n    <string name=\"About\">Kohta</string>\n    <string name=\"report_issue\">Teata probleemist</string>\n    <string name=\"module_repo\">Repo</string>\n    <string name=\"module_repo_up_to_date\">Kõik moodulid on ajakohased</string>\n    <string name=\"module_repo_published_time\">Avaldatud %s</string>\n    <string name=\"module_repo_updated_time\">Uuendatud %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d moodul on täiendatav</item>\n        <item quantity=\"other\">%d moodulit täiendatavad</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Vaata lähtekoodi aadressil %1$s<br/>Liitu meie %2$s \\'i kanaliga]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Subaru Pan</string>\n    <string name=\"install\">Installi</string>\n    <string name=\"install_summary\">Puudutage LSPosedi installimiseks</string>\n    <string name=\"not_installed\">Puudub</string>\n    <string name=\"not_install_summary\">LSPosed ei ole installitud</string>\n    <string name=\"activated\">Aktiveeritud</string>\n    <string name=\"partial_activated\">Osaliselt aktiveeritud</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy ei ole korralikult laetud</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Mõned funktsioonid ei tööta.<br/>Palun teatage sellest <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> arendajale.]]></string>\n    <string name=\"system_inject_fail_summary\">System Frameworki süstimine ebaõnnestus</string>\n    <string name=\"system_inject_fail\"><![CDATA[Seda esineb harva ja selle põhjuseks võib olla <b>Magisk</b> või mõned madala kvaliteediga Magisk-moodulid.<br/>Palun proovige lülitada välja Magisk\\'i moodulid peale Riru ja LSPosed või esitage täielik logi arendajatele.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Süsteemi tugi vale</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Mõned vajalikud süsteemiomadused kustutatud või muudetud.<br/>Moodulid võivad aeg-ajalt kehtetuks muutuda.]]></string>\n    <string name=\"need_update\">Vaja uuendada</string>\n    <string name=\"please_update_summary\">Palun installige LSPosedi uusim versioon</string>\n    <string name=\"info_api_version\">API\\'i versioon</string>\n    <string name=\"info_framework_version\">Raamistiku versioon</string>\n    <string name=\"info_manager_package_name\">Manager paketi nimi</string>\n    <string name=\"info_system_version\">Süsteemi versioon</string>\n    <string name=\"info_device\">Seade</string>\n    <string name=\"info_system_abi\">Süsteemi ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Lubatud</string>\n    <string name=\"not_enabled\">Pole lubatud</string>\n    <string name=\"supported\">Toetatud</string>\n    <string name=\"unsupported\">Toetamata</string>\n    <string name=\"android_version_unsatisfied\">Androidi versioon ei ole toetatud</string>\n    <string name=\"crashed\">Kokku jooksnud</string>\n    <string name=\"mount_failed\">Kinnitus ebaõnnestus</string>\n    <string name=\"selinux_permissive\">SELinux on lubav</string>\n    <string name=\"sepolicy_incorrect\">SELinux policy on vale</string>\n    <string name=\"update_lsposed\">LSPosedi uuendamine</string>\n    <string name=\"update_lsposed_msg\">Kas kinnitada LSPosed uuendamine? See seade taaskäivitub pärast uuendamise lõpetamist</string>\n    <string name=\"info_copied\">Kopeeritud lõikelauale</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Tere tulemast LSPosedisse</string>\n    <string name=\"parasitic_welcome_summary\">Sa kasutad parasiitide haldurit, mis võib luua otsetee või ikka avada teateid.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Kasutate parasiitide haldurit, mida saab avada teatisest.</string>\n    <string name=\"create_shortcut\">Loo otsetee</string>\n    <string name=\"never_show\">Mitte kunagi ei näidata</string>\n    <string name=\"app_welcome\">Parasiitide haldur Soovitatav</string>\n    <string name=\"app_welcome_summary\">LSPosed toetab nüüd süsteemi parasiitide tuvastamise vältimiseks, saate avada parasiitide haldaja teatest. Praegune rakendus on soovitatav eemaldada.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Salvesta</string>\n    <string name=\"nav_item_logs_verbose\">Põhjalikud logid</string>\n    <string name=\"nav_item_logs_module\">Moodulite logid</string>\n    <string name=\"logs_saving\">Logi salvestamine, palun oodake</string>\n    <string name=\"logs_saved\">Logi salvestatud</string>\n    <string name=\"logs_save_failed2\">Salvestamine ebaõnnestus:\\n%s</string>\n    <string name=\"menuClearLog\">Kustuta logi kohe</string>\n    <string name=\"logs_cleared\">Logi edukalt kustutatud.</string>\n    <string name=\"scroll_top\">Kerige üles</string>\n    <string name=\"loading\">Laadimine…</string>\n    <string name=\"scroll_bottom\">Kerige alla</string>\n    <string name=\"menuReload\">Laadi uuesti</string>\n    <string name=\"logs_clear_failed_2\">Logi tühjendamine ebaõnnestus</string>\n    <string name=\"menu_enable_word_wrap\">Word Wrap</string>\n    <string name=\"enabled_verbose_log\">Paljusõnaline logi on lubatud</string>\n    <string name=\"disabled_verbose_log\">Paljusõnaline logi on välja lülitatud</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(kirjeldus puudub)</string>\n    <string name=\"warning_xposed_min_version\">See moodul nõuab uuemat Xposed versiooni (%d) ja seega ei saa seda aktiveerida.</string>\n    <string name=\"warning_target_version_higher\">See moodul on mõeldud uuemale Xposedi versioonile (%d) ja seetõttu ei pruugi mõned funktsioonid töötada</string>\n    <string name=\"no_min_version_specified\">See moodul ei täpsusta Xposedi versiooni, mida ta vajab.</string>\n    <string name=\"warning_min_version_too_low\">See moodul loodi Xposedi versiooni %1$d jaoks, kuid versioonis %2$d tehtud ühildumatute muudatuste tõttu on see välja lülitatud</string>\n    <string name=\"warning_installed_on_external_storage\">Seda moodulit ei saa laadida, sest see on paigaldatud SD-kaardile, palun viige see sisemällu.</string>\n    <string name=\"module_uninstall\">Eemalda</string>\n    <string name=\"module_settings\">Mooduli seaded</string>\n    <string name=\"view_in_repo\">Vaata Repos</string>\n    <string name=\"module_uninstall_message\">Kas soovite selle mooduli eemaldada?</string>\n    <string name=\"module_uninstalled\">Eemaldatud %1$s</string>\n    <string name=\"module_uninstall_failed\">Eemaldamine ebaõnnestus</string>\n    <string name=\"add_module_to_user\">Lisa kasutajale moodul</string>\n    <string name=\"module_installed\">Lisatud %1$s kasutajale %2$s</string>\n    <string name=\"module_install_failed\">Mooduli lisamine ebaõnnestus</string>\n    <string name=\"install_to_user\">Installi kasutajale %s</string>\n    <string name=\"install_to_user_message\">Tahad paigaldada %1$s kasutajale %2$s? Soovitatav on paigaldada käsitsi, LSPosed\\'i kaudu sunniviisiline paigaldamine võib põhjustada probleeme.</string>\n    <string name=\"expand\">laienda</string>\n    <string name=\"collapse\">kollaps</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Optimeeri uuesti</string>\n    <string name=\"compile_speed_msg\">Optimeerimine…</string>\n    <string name=\"compile_done\">Optimeeritud</string>\n    <string name=\"app_launch\">Ava</string>\n    <string name=\"compile_failed\">Optimeerimine ebaõnnestus: tagastusväärtus on tühi</string>\n    <string name=\"compile_failed_with_info\">Optimeerimine ebaõnnestus: </string>\n    <string name=\"sort_by_name\">Rakenduse nimi</string>\n    <string name=\"sort_by_package_name\">Paketi nimi</string>\n    <string name=\"sort_by_install_time\">Installimise aeg</string>\n    <string name=\"sort_by_update_time\">Uuendamise aeg</string>\n    <string name=\"sort_reverse\">Tagasipööra</string>\n    <string name=\"menu_show_system_apps\">Süsteemirakendused</string>\n    <string name=\"menu_sort\">Sortimisalus</string>\n    <string name=\"enable_module\">Luba moodul</string>\n    <string name=\"no_scope_selected\">Te ei valinud ühtegi rakendust. Jätka?</string>\n    <string name=\"menu_show_games\">Mängud</string>\n    <string name=\"menu_show_modules\">Moodulid</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Ei õnnestunud salvestada reguleerimisala nimekirja</string>\n    <string name=\"app_version\">Versioon: %1$s</string>\n    <string name=\"use_recommended\">Soovitatav</string>\n    <string name=\"no_scope_selected_has_recommended\">Te ei valinud ühtegi rakendust. Valige soovitatud rakendused?</string>\n    <string name=\"use_recommended_message\">Valige soovitatavad rakendused?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed moodul ei ole aktiveeritud</string>\n    <string name=\"requested_by_module\">Soovitatav</string>\n    <string name=\"update_available\">Uuendus on saadaval: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Moodul %s on välja lülitatud, kuna ühtegi rakendust ei ole valitud.</string>\n    <string name=\"android_framework\">Süsteemi raamistik</string>\n    <string name=\"menu_backup_and_restore\">Varukoopia</string>\n    <string name=\"menu_backup\">Varukoopia</string>\n    <string name=\"menu_restore\">Taasta</string>\n    <string name=\"force_stop\">Sundpeata</string>\n    <string name=\"force_stop_dlg_title\">Sundpeata?</string>\n    <string name=\"force_stop_dlg_text\">Kui te peatate rakenduse sunniviisiliselt, võib see halvasti käituda.</string>\n    <string name=\"reboot_required\">Selle muudatuse kohaldamiseks on vajalik taaskäivitamine</string>\n    <string name=\"reboot\">Taaskäivitus</string>\n    <string name=\"menu_hide\">Peida</string>\n    <string name=\"deny_list\">%s on denylistis. See ei pruugi jõustuda.</string>\n    <string name=\"deny_list_info\">Denylistil</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Vaadake teises rakenduses</string>\n    <string name=\"module_app_info\">Rakenduse teave</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nSiin pole midagi.</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Raamistik</string>\n    <string name=\"settings_disable_verbose_log\">Lülita sõnalised logid välja</string>\n    <string name=\"settings_disable_verbose_log_summary\">Aruande probleemid taotluse lisada sõnalogid</string>\n    <string name=\"pure_black_dark_theme\">Must tume teema</string>\n    <string name=\"pure_black_dark_theme_summary\">Kasutage puhast musta teemat, kui tume teema on lubatud.</string>\n    <string name=\"settings_group_theme\">Teema</string>\n    <string name=\"settings_backup_and_restore\">Varundamine ja taastamine</string>\n    <string name=\"settings_backup_summery\">Moodulite varukoopiate nimekiri ja ulatusloendid.</string>\n    <string name=\"settings_restore_summery\">Taastab moodulite loendi ja ulatusloendite loendi.</string>\n    <string name=\"settings_backup\">Varukoopia</string>\n    <string name=\"settings_backup_failed2\">Varundamine ebaõnnestus:\\n%s</string>\n    <string name=\"enable_documentui\">Palun lubage DocumentUI</string>\n    <string name=\"settings_restore\">Taasta</string>\n    <string name=\"settings_restore_failed2\">Ei õnnestunud taastada:\\n%s</string>\n    <string name=\"group_network\">Võrk</string>\n    <string name=\"dns_over_http\">DNS üle HTTPS</string>\n    <string name=\"dns_over_http_summary\">Workaround DNS mürgistus mõnedes riikides</string>\n    <string name=\"theme_color\">Teema värv</string>\n    <string name=\"theme_color_system\">Süsteemi teema värv</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Rakenduste sundimine käivitaja ikoonide kuvamiseks</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Pärast Android 10 ei ole rakendustel lubatud oma käivitaja ikoonid ära peita. Selle süsteemifunktsiooni väljalülitamiseks lülitage lüliti välja.</string>\n    <string name=\"settings_group_system\">Süsteem</string>\n    <string name=\"settings_language\">Keel</string>\n    <string name=\"settings_translation_contributors\">Tõlkimise toetajad</string>\n    <string name=\"settings_translation\">Osalege tõlkimises</string>\n    <string name=\"settings_translation_summary\">Aita meil tõlkida %s sinu keelde</string>\n    <string name=\"settings_create_shortcut_summary\">Loo otsetee, mis võib avada parasiitide halduri</string>\n    <string name=\"settings_shortcut_pinned_hint\">Otsetee kinnitatud</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Praegune vaikekäivitusprogramm ei toeta nööpnõelte otseteid</string>\n    <string name=\"settings_enable_status_notification\">Staatuse teatamine</string>\n    <string name=\"settings_enable_status_notification_summary\">Kuva teatis, mis võib avada parasiitide halduri</string>\n    <string name=\"disable_status_notification_error\">Otsetee puudub, teavitust ei saa keelata</string>\n    <string name=\"settings_update_channel\">Uuenduskanal</string>\n    <string name=\"update_channel_stable\">Stabiilne</string>\n    <string name=\"update_channel_bate\">Beeta</string>\n    <string name=\"update_channel_nightly\">Nightly build</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API call protection</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokeerige dünaamiliselt laaditud mooduli kood, et kasutada Xposed API-t, see võib mõne mooduli rikkuda, kuid toob kasu turvalisusele</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Väljaanded</string>\n    <string name=\"module_information\">Teave</string>\n    <string name=\"module_information_homepage\">Koduleht</string>\n    <string name=\"module_information_source_url\">Lähtekood</string>\n    <string name=\"module_information_collaborators\">Koostööpartnerid</string>\n    <string name=\"module_release_view_assets\">Varad</string>\n    <string name=\"menu_open_in_browser\">Ava brauseris</string>\n    <string name=\"module_release_load_more\">Kuva vanemad versioonid</string>\n    <string name=\"module_release_no_more\">Enam ei ole väljaannet</string>\n    <string name=\"repo_load_failed\">Ebaõnnestus mooduli repo laadimine: %s</string>\n    <string name=\"sort_upgradable_first\">Esimesena uuendatav</string>\n    <string name=\"installed\">Paigaldatud</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">allalaaditud on %d kord</item>\n        <item quantity=\"other\">allalaaditud on %d korda</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Punane</string>\n    <string name=\"color_pink\">Roosa</string>\n    <string name=\"color_purple\">Lilla</string>\n    <string name=\"color_deep_purple\">Sügavlilla</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Sinine</string>\n    <string name=\"color_light_blue\">Helesinine</string>\n    <string name=\"color_cyan\">Tsüaansinine</string>\n    <string name=\"color_teal\">Sinakasroheline</string>\n    <string name=\"color_green\">Roheline</string>\n    <string name=\"color_light_green\">Heleroheline</string>\n    <string name=\"color_lime\">Laimiroheline</string>\n    <string name=\"color_yellow\">Kollane</string>\n    <string name=\"color_amber\">Amber</string>\n    <string name=\"color_orange\">Oranž</string>\n    <string name=\"color_deep_orange\">Sügavoranž</string>\n    <string name=\"color_brown\">Pruun</string>\n    <string name=\"color_blue_grey\">Sinine hall</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fa/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">نمای کلی</string>\n    <string name=\"Modules\">ماژول‌ها</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d ماژول فعال</item>\n        <item quantity=\"other\">%d ماژول فعال</item>\n    </plurals>\n    <string name=\"Logs\">لاگ ها</string>\n    <string name=\"Settings\">تنظیمات</string>\n    <string name=\"feedback_or_suggestion\">بازخورد یا پیشنهاد</string>\n    <string name=\"About\">درباره</string>\n    <string name=\"report_issue\">گزارش مشکل</string>\n    <string name=\"module_repo\">مخزن</string>\n    <string name=\"module_repo_up_to_date\">همه ماژول ها بروز هستند</string>\n    <string name=\"module_repo_published_time\">منتشر شده در %s</string>\n    <string name=\"module_repo_updated_time\">بروزرسانی شده در %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d ماژول قابل بروزرسانی</item>\n        <item quantity=\"other\">%d ماژول قابل بروزرسانی</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[دیدن سورس کد در %1$s<br/>عضو کانال %2$s شوید]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">نصب</string>\n    <string name=\"install_summary\">برای نصب LSPosed لمس کنید</string>\n    <string name=\"not_installed\">نصب نشده</string>\n    <string name=\"not_install_summary\">LSPosed نصب نشده</string>\n    <string name=\"activated\">فعال شده</string>\n    <string name=\"partial_activated\">نیمه فعال</string>\n    <string name=\"selinux_policy_not_loaded_summary\">سیاست SELinux به درستی بارگذاری نشده</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[برخی قابلیت ها کار نمی کنند.<br/>لطفاً این موضوع را به توسعه دهنده <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> گزارش دهید.]]></string>\n    <string name=\"system_inject_fail_summary\">تزریق به چارچوب سیستم ناموفق بود</string>\n    <string name=\"system_inject_fail\"><![CDATA[این مشکل نادر است و ممکن است به خاطر <b>Magisk</b> یا برخی ماژول های بی کیفیت Magisk باشد.<br/>لطفاً ماژول های Magisk به جز Riru و LSPosed را غیرفعال کنید یا لاگ کامل را برای توسعه دهندگان بفرستید.]]></string>\n    <string name=\"system_prop_incorrect_summary\">ویژگی های سیستم نادرست است</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[برخی ویژگی های ضروری سیستم حذف یا تغییر کرده اند.<br/>ماژول ها ممکن است گاهی کار نکنند.]]></string>\n    <string name=\"need_update\">نیاز به بروزرسانی</string>\n    <string name=\"please_update_summary\">LSPosedLSPosed</string>\n    <string name=\"developer_warning_title\">نکات برای توسعه دهنده ماژول</string>\n    <string name=\"developer_warning_summary\">لطفاً بهینه‌سازی‌های استقرار را در اندروید استودیو خاموش کنید یا از دستور `gradlew installDebug` استفاده کنید. در غیر این صورت APK ماژول آپدیت نمی‌شود.</string>\n    <string name=\"info_api_version\">نسخه API</string>\n    <string name=\"info_framework_version\">نسخه چارچوب</string>\n    <string name=\"info_manager_package_name\">نام پکیج مدیر</string>\n    <string name=\"info_system_version\">نسخه سیستم</string>\n    <string name=\"info_device\">دستگاه</string>\n    <string name=\"info_system_abi\">ABI سیستم</string>\n    <string name=\"info_dex2oat_wrapper\">قالب بهینه‌سازی Dex</string>\n    <string name=\"enabled\">فعال</string>\n    <string name=\"not_enabled\">غیرفعال</string>\n    <string name=\"supported\">پشتیبانی شده</string>\n    <string name=\"unsupported\">پشتیبانی نمی شود</string>\n    <string name=\"android_version_unsatisfied\">نسخه اندروید پشتیبانی نمی شود</string>\n    <string name=\"crashed\">کرش کرد</string>\n    <string name=\"mount_failed\">مونت ناموفق بود</string>\n    <string name=\"selinux_permissive\">SELinux در حالت Permissive است</string>\n    <string name=\"sepolicy_incorrect\">سیاست SELinux نادرست است</string>\n    <string name=\"update_lsposed\">بروزرسانی LSPosed</string>\n    <string name=\"update_lsposed_msg\">آیا بروزرسانی LSPosed را تأیید می کنید؟ بعد از پایان، دستگاه ری استارت می شود</string>\n    <string name=\"info_copied\">کپی شد</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">به LSPosed خوش آمدید</string>\n    <string name=\"parasitic_welcome_summary\">شما از مدیر Parasitic استفاده می کنید که می تواند شورتکات بسازد یا از نوتیفیکیشن باز شود.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">شما از مدیر Parasitic استفاده می کنید که فقط از نوتیفیکیشن باز می شود.</string>\n    <string name=\"create_shortcut\">ساخت شورتکات</string>\n    <string name=\"never_show\">هرگز نمایش نده</string>\n    <string name=\"app_welcome\">مدیر Parasitic توصیه شده</string>\n    <string name=\"app_welcome_summary\">LSPosed حالا از سیستم Parasitic پشتیبانی می کند تا شناسایی نشود، می توانید از نوتیفیکیشن مدیر Parasitic را باز کنید. بهتر است برنامه فعلی را حذف کنید.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">ذخیره</string>\n    <string name=\"nav_item_logs_verbose\">لاگ های مفصل</string>\n    <string name=\"nav_item_logs_module\">لاگ های ماژول</string>\n    <string name=\"logs_saving\">در حال ذخیره لاگ، لطفاً صبر کنید</string>\n    <string name=\"logs_saved\">لاگ ها ذخیره شدند</string>\n    <string name=\"logs_save_failed2\">ذخیره موفق نبود:\\n%s</string>\n    <string name=\"menuClearLog\">همین الان لاگ ها را پاک کن</string>\n    <string name=\"logs_cleared\">لاگ ها با موفقیت پاک شدند.</string>\n    <string name=\"scroll_top\">برگشت به بالا</string>\n    <string name=\"loading\">در حال بارگذاری…</string>\n    <string name=\"scroll_bottom\">رفتن به پایین</string>\n    <string name=\"menuReload\">بارگذاری مجدد</string>\n    <string name=\"logs_clear_failed_2\">پاک کردن لاگ ناموفق بود</string>\n    <string name=\"menu_enable_word_wrap\">شکستن خودکار خطوط</string>\n    <string name=\"enabled_verbose_log\">لاگ مفصل فعال شد</string>\n    <string name=\"disabled_verbose_log\">لاگ مفصل غیرفعال شد</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(توضیحی داده نشده)</string>\n    <string name=\"warning_xposed_min_version\">این ماژول نیاز به نسخه جدیدتر Xposed (%d) دارد و نمی تواند فعال شود</string>\n    <string name=\"warning_target_version_higher\">این ماژول برای نسخه جدیدتر Xposed (%d) ساخته شده، پس ممکن است برخی امکانات کار نکنند</string>\n    <string name=\"no_min_version_specified\">این ماژول نسخه Xposed مورد نیازش را مشخص نکرده.</string>\n    <string name=\"warning_min_version_too_low\">این ماژول برای نسخه %1$d ساخته شده، اما به دلیل تغییرات ناسازگار در نسخه %2$d غیرفعال شده</string>\n    <string name=\"warning_installed_on_external_storage\">این ماژول نمی تواند بارگذاری شود چون روی کارت حافظه نصب شده، لطفاً به حافظه داخلی منتقل کنید</string>\n    <string name=\"module_uninstall\">حذف نصب</string>\n    <string name=\"module_settings\">تنظیمات ماژول</string>\n    <string name=\"view_in_repo\">مشاهده در مخزن</string>\n    <string name=\"module_uninstall_message\">می خواهید این ماژول را حذف کنید؟</string>\n    <string name=\"module_uninstalled\">حذف شد %1$s</string>\n    <string name=\"module_uninstall_failed\">حذف موفق نبود</string>\n    <string name=\"add_module_to_user\">اضافه کردن ماژول به کاربر</string>\n    <string name=\"module_installed\">اضافه شد %1$s به کاربر %2$s</string>\n    <string name=\"module_install_failed\">اضافه کردن ماژول موفق نبود</string>\n    <string name=\"install_to_user\">نصب برای کاربر %s</string>\n    <string name=\"install_to_user_message\">می خواهید %1$s را برای کاربر %2$s نصب کنید؟ توصیه می شود دستی نصب کنید، نصب با LSPosed ممکن است مشکل ایجاد کند.</string>\n    <string name=\"expand\">باز کن</string>\n    <string name=\"collapse\">ببند</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">بهینه‌سازی مجدد</string>\n    <string name=\"compile_speed_msg\">در حال بهینه‌سازی…</string>\n    <string name=\"compile_done\">بهینه‌سازی تمام شد</string>\n    <string name=\"app_launch\">باز کن</string>\n    <string name=\"compile_failed\">بهینه‌سازی شکست خورد: خروجی خالی است</string>\n    <string name=\"compile_failed_with_info\">بهینه‌سازی شکست خورد: </string>\n    <string name=\"sort_by_name\">نام برنامه</string>\n    <string name=\"sort_by_package_name\">نام پکیج</string>\n    <string name=\"sort_by_install_time\">زمان نصب</string>\n    <string name=\"sort_by_update_time\">زمان بروزرسانی</string>\n    <string name=\"sort_reverse\">معکوس</string>\n    <string name=\"menu_show_system_apps\">برنامه های سیستمی</string>\n    <string name=\"menu_sort\">مرتب‌سازی</string>\n    <string name=\"enable_module\">فعال کردن ماژول</string>\n    <string name=\"no_scope_selected\">برنامه ای انتخاب نکردی، ادامه میدی؟</string>\n    <string name=\"menu_show_games\">بازی ها</string>\n    <string name=\"menu_show_modules\">ماژول ها</string>\n    <string name=\"menu_show_denylist\">لیست مسدود</string>\n    <string name=\"failed_to_save_scope_list\">ذخیره لیست ناموفق بود</string>\n    <string name=\"app_version\">نسخه: %1$s</string>\n    <string name=\"menu_select\">انتخاب</string>\n    <string name=\"use_recommended\">توصیه شده</string>\n    <string name=\"no_scope_selected_has_recommended\">برنامه ای انتخاب نکردی. برنامه های توصیه شده را انتخاب کنم؟</string>\n    <string name=\"use_recommended_message\">می خوای برنامه های توصیه شده رو انتخاب کنی؟</string>\n    <string name=\"menu_select_all\">همه</string>\n    <string name=\"menu_select_none\">هیچی</string>\n    <string name=\"menu_auto_include\">شامل خودکار</string>\n    <string name=\"module_is_not_activated_yet\">ماژول Xposed هنوز فعال نشده</string>\n    <string name=\"requested_by_module\">توصیه شده</string>\n    <string name=\"update_available\">بروزرسانی موجود: %1$s</string>\n    <string name=\"module_disabled_no_selection\">ماژول %s به خاطر انتخاب نکردن برنامه غیرفعال شده.</string>\n    <string name=\"android_framework\">چارچوب سیستم</string>\n    <string name=\"menu_backup_and_restore\">پشتیبان گیری</string>\n    <string name=\"menu_backup\">پشتیبان گیری</string>\n    <string name=\"menu_restore\">بازیابی</string>\n    <string name=\"force_stop\">توقف اجباری</string>\n    <string name=\"force_stop_dlg_title\">توقف اجباری؟</string>\n    <string name=\"force_stop_dlg_text\">اگر برنامه را به زور متوقف کنی، ممکن است درست کار نکند.</string>\n    <string name=\"reboot_required\">برای اعمال تغییر باید ری استارت کنی</string>\n    <string name=\"reboot\">ری استارت</string>\n    <string name=\"menu_hide\">مخفی کن</string>\n    <string name=\"deny_list\">%s در لیست مسدود است. ممکن است کار نکند.</string>\n    <string name=\"deny_list_info\">در لیست مسدود</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">مشاهده در برنامه دیگر</string>\n    <string name=\"module_app_info\">اطلاعات برنامه</string>\n    <string name=\"list_empty\">¯\\_(ツ)_/¯\\nاینجا چیزی نیست</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">چارچوب</string>\n    <string name=\"settings_disable_verbose_log\">غیرفعال کردن لاگ مفصل</string>\n    <string name=\"settings_disable_verbose_log_summary\">لاگ مفصل برای گزارش مشکل لازم است</string>\n    <string name=\"settings_enable_log_watchdog\">فعال کردن نظارت لاگ</string>\n    <string name=\"settings_enable_log_watchdog_summary\">نظارت لاگ LSPosed ویژگی های سیستم را تغییر می دهد و ممکن است برای شناسایی LSPosed استفاده شود</string>\n    <string name=\"pure_black_dark_theme\">تم سیاه کامل</string>\n    <string name=\"pure_black_dark_theme_summary\">اگر تم تاریک فعال است از تم کاملا سیاه استفاده کن</string>\n    <string name=\"settings_group_theme\">تم</string>\n    <string name=\"settings_backup_and_restore\">پشتیبان گیری و بازیابی</string>\n    <string name=\"settings_backup_summery\">پشتیبان گیری از لیست ماژول ها و برنامه ها.</string>\n    <string name=\"settings_restore_summery\">بازیابی لیست ماژول ها و برنامه ها.</string>\n    <string name=\"settings_backup\">پشتیبان گیری</string>\n    <string name=\"settings_backup_failed2\">پشتیبان گیری ناموفق بود:\\n%s</string>\n    <string name=\"enable_documentui\">لطفاً DocumentUI را فعال کنید</string>\n    <string name=\"settings_restore\">بازیابی</string>\n    <string name=\"settings_restore_failed2\">بازیابی ناموفق بود:\\n%s</string>\n    <string name=\"group_network\">شبکه</string>\n    <string name=\"dns_over_http\">DNS روی HTTPS</string>\n    <string name=\"dns_over_http_summary\">حل مشکل مسمومیت DNS در بعضی کشورها</string>\n    <string name=\"theme_color\">رنگ تم</string>\n    <string name=\"theme_color_system\">رنگ تم سیستم</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">نمایش آیکون های لانچر برنامه ها</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">از اندروید ۱۰ به بعد، برنامه ها نمی توانند آیکون لانچر را مخفی کنند. این گزینه را خاموش کن تا این ویژگی غیرفعال شود.</string>\n    <string name=\"settings_group_system\">سیستم</string>\n    <string name=\"settings_language\">زبان</string>\n    <string name=\"settings_translation_contributors\">مشارکت کنندگان ترجمه</string>\n    <string name=\"settings_translation\">مشارکت در ترجمه</string>\n    <string name=\"settings_translation_summary\">کمک کن %s را به زبان خودت ترجمه کنیم</string>\n    <string name=\"settings_create_shortcut_summary\">شورتکاتی بساز که مدیر Parasitic را باز کند</string>\n    <string name=\"settings_shortcut_pinned_hint\">شورتکات پین شد</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">لانچر پیش فرض فعلی شورتکات های پین شده را پشتیبانی نمی کند</string>\n    <string name=\"settings_enable_status_notification\">نمایش اعلان وضعیت</string>\n    <string name=\"settings_enable_status_notification_summary\">نمایش اعلانی که مدیر Parasitic را باز کند</string>\n    <string name=\"disable_status_notification_error\">شورتکات نیست، نمی توان اعلان را غیرفعال کرد</string>\n    <string name=\"settings_update_channel\">کانال بروزرسانی</string>\n    <string name=\"update_channel_stable\">پایدار</string>\n    <string name=\"update_channel_bate\">بتا</string>\n    <string name=\"update_channel_nightly\">نسخه شبانه</string>\n    <string name=\"settings_xposed_api_call_protection\">حفاظت از تماس API Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">جلوگیری از استفاده کد ماژول های بارگذاری شده به صورت داینامیک از API های Xposed، ممکن است بعضی ماژول ها کار نکنند ولی امنیت بهتر می شود</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">راهنما</string>\n    <string name=\"module_releases\">نسخه ها</string>\n    <string name=\"module_information\">اطلاعات</string>\n    <string name=\"module_information_homepage\">صفحه اصلی</string>\n    <string name=\"module_information_source_url\">سورس کد</string>\n    <string name=\"module_information_collaborators\">همکاران</string>\n    <string name=\"module_release_view_assets\">دارایی ها</string>\n    <string name=\"menu_open_in_browser\">باز کردن در مرورگر</string>\n    <string name=\"module_release_load_more\">نمایش نسخه های قدیمی تر</string>\n    <string name=\"module_release_no_more\">نسخه ای بیشتر نیست</string>\n    <string name=\"repo_load_failed\">بارگذاری مخزن ماژول شکست خورد: %s</string>\n    <string name=\"sort_upgradable_first\">اول ماژول های قابل بروزرسانی</string>\n    <string name=\"installed\">نصب شده</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d دانلود</item>\n        <item quantity=\"other\">%d دانلود</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">ساکورا</string>\n    <string name=\"color_red\">قرمز</string>\n    <string name=\"color_pink\">صورتی</string>\n    <string name=\"color_purple\">بنفش</string>\n    <string name=\"color_deep_purple\">بنفش تیره</string>\n    <string name=\"color_indigo\">نیلی</string>\n    <string name=\"color_blue\">آبی</string>\n    <string name=\"color_light_blue\">آبی روشن</string>\n    <string name=\"color_cyan\">آبی فیروزه ای</string>\n    <string name=\"color_teal\">آبی خاکستری</string>\n    <string name=\"color_green\">سبز</string>\n    <string name=\"color_light_green\">سبز روشن</string>\n    <string name=\"color_lime\">لیمویی</string>\n    <string name=\"color_yellow\">زرد</string>\n    <string name=\"color_amber\">کهربایی</string>\n    <string name=\"color_orange\">نارنجی</string>\n    <string name=\"color_deep_orange\">نارنجی تیره</string>\n    <string name=\"color_brown\">قهوه ای</string>\n    <string name=\"color_blue_grey\">آبی خاکستری</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Yleiskatsaus</string>\n    <string name=\"Modules\">Moduulit</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d moduuli käytössä</item>\n        <item quantity=\"other\">%d moduulia käytössä</item>\n    </plurals>\n    <string name=\"Logs\">Lokit</string>\n    <string name=\"Settings\">Asetukset</string>\n    <string name=\"feedback_or_suggestion\">Palaute tai ehdotus</string>\n    <string name=\"About\">Tietoja</string>\n    <string name=\"report_issue\">Ilmoita ongelmasta</string>\n    <string name=\"module_repo\">Versiovarasto</string>\n    <string name=\"module_repo_up_to_date\">Kaikki moduulit ajan tasalla</string>\n    <string name=\"module_repo_published_time\">Julkaistu osoitteessa %s</string>\n    <string name=\"module_repo_updated_time\">Päivitetty osoitteessa %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d moduuli päivitettävissä</item>\n        <item quantity=\"other\">%d moduulia päivitettävissä</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Näytä lähdekoodi %1$s<br/>Liity kanavaamme %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Asenna</string>\n    <string name=\"install_summary\">Napauta asentaaksesi LSPosed</string>\n    <string name=\"not_installed\">Ei asennettu</string>\n    <string name=\"not_install_summary\">LSPosed ei ole asennettu</string>\n    <string name=\"activated\">Aktivoitu</string>\n    <string name=\"partial_activated\">Osittain aktivoitu</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy ei ole ladattu oikein</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Jotkin ominaisuudet eivät toimi.<br/>Ilmoita tästä <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> kehittäjälle.]]></string>\n    <string name=\"system_inject_fail_summary\">Järjestelmän kehysinjektointi epäonnistui</string>\n    <string name=\"system_inject_fail\"><![CDATA[Tämä on harvinaista ja voi johtua <b>Magisk</b> tai joitakin heikkolaatuisia Magisk moduuleja.<br/>Yritä poistaa käytöstä muut Magisk moduulit kuin Riru ja LSPosed tai lähettää täysi loki kehittäjille.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Järjestelmän prop virheellinen</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Jotkut tarvittavat järjestelmän ominaisuudet poistettu tai muokattu.<br/>Moduulit voivat mitätöidä satunnaisesti.]]></string>\n    <string name=\"need_update\">Täytyy päivittää</string>\n    <string name=\"please_update_summary\">Asenna LSPosedin uusin versio</string>\n    <string name=\"info_api_version\">API versio</string>\n    <string name=\"info_framework_version\">Kehyksen versio</string>\n    <string name=\"info_manager_package_name\">Manager-paketin nimi</string>\n    <string name=\"info_system_version\">Järjestelmän versio</string>\n    <string name=\"info_device\">Laite</string>\n    <string name=\"info_system_abi\">Järjestelmä ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Käytössä</string>\n    <string name=\"not_enabled\">Ei käytössä</string>\n    <string name=\"supported\">Tuettu</string>\n    <string name=\"unsupported\">Ei tuettu</string>\n    <string name=\"android_version_unsatisfied\">Android-versio tyytymätön</string>\n    <string name=\"crashed\">Crashed</string>\n    <string name=\"mount_failed\">Kiinnitys epäonnistui</string>\n    <string name=\"selinux_permissive\">SELinux on salliva</string>\n    <string name=\"sepolicy_incorrect\">SELinux-käytäntö on virheellinen</string>\n    <string name=\"update_lsposed\">Päivitys LSPostettu</string>\n    <string name=\"update_lsposed_msg\">Vahvista LSPost-päivitys? Tämä laite käynnistyy uudelleen päivityksen jälkeen</string>\n    <string name=\"info_copied\">Kopioitu leikepöydälle</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Tervetuloa LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Käytät loishallintaohjelmaa, joka voi luoda pikakuvakkeen tai silti avata ilmoituksen.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Käytät loishallintaohjelmaa, joka voidaan avata ilmoituksesta.</string>\n    <string name=\"create_shortcut\">Luo pikakuvake</string>\n    <string name=\"never_show\">Älä näytä koskaan</string>\n    <string name=\"app_welcome\">Parasitic Manager Suositellaan</string>\n    <string name=\"app_welcome_summary\">LSPosed tukee nyt järjestelmän loisimista havaitsemisen välttämiseksi, voit avata loishallinnan ilmoituksesta. On suositeltavaa poistaa nykyinen sovellus.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Tallenna</string>\n    <string name=\"nav_item_logs_verbose\">Verbose Lokit</string>\n    <string name=\"nav_item_logs_module\">Moduulien Lokit</string>\n    <string name=\"logs_saving\">Lokin tallentaminen, odota</string>\n    <string name=\"logs_saved\">Tallennetut lokit</string>\n    <string name=\"logs_save_failed2\">Tallennus epäonnistui:\\n%s</string>\n    <string name=\"menuClearLog\">Tyhjennä loki nyt</string>\n    <string name=\"logs_cleared\">Loki tyhjennetty.</string>\n    <string name=\"scroll_top\">Vieritä ylös</string>\n    <string name=\"loading\">Ladataan…</string>\n    <string name=\"scroll_bottom\">Siirry alareunaan</string>\n    <string name=\"menuReload\">Reload</string>\n    <string name=\"logs_clear_failed_2\">Lokin tyhjentäminen epäonnistui</string>\n    <string name=\"menu_enable_word_wrap\">Sanan Rivitys</string>\n    <string name=\"enabled_verbose_log\">Verbose loki käytössä</string>\n    <string name=\"disabled_verbose_log\">Verbose loki pois käytöstä</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(ei kuvausta annettu)</string>\n    <string name=\"warning_xposed_min_version\">Tämä moduuli vaatii uudemman Xposed version (%d) eikä sitä näin ollen voi aktivoida</string>\n    <string name=\"warning_target_version_higher\">Tämä moduuli on suunniteltu uudemmalle Xposed-versiolle (%d), joten jotkin toiminnot eivät välttämättä toimi.</string>\n    <string name=\"no_min_version_specified\">Tämä moduuli ei määrittele tarvitsemaansa Xposed versiota.</string>\n    <string name=\"warning_min_version_too_low\">Tämä moduuli on luotu Xposed versiolle %1$d, mutta koska versiossa %2$don tehty yhteensopimattomia muutoksia, se on poistettu käytöstä</string>\n    <string name=\"warning_installed_on_external_storage\">Tätä moduulia ei voi ladata, koska se on asennettu SD-kortille, siirrä se sisäiseen tallennustilaan</string>\n    <string name=\"module_uninstall\">Poista</string>\n    <string name=\"module_settings\">Moduulin asetukset</string>\n    <string name=\"view_in_repo\">Näytä repossa</string>\n    <string name=\"module_uninstall_message\">Haluatko poistaa tämän moduulin?</string>\n    <string name=\"module_uninstalled\">Poista %1$s</string>\n    <string name=\"module_uninstall_failed\">Poisto epäonnistui</string>\n    <string name=\"add_module_to_user\">Lisää moduuli käyttäjälle</string>\n    <string name=\"module_installed\">Lisätty %1$s käyttäjälle %2$s</string>\n    <string name=\"module_install_failed\">Moduulin lisääminen epäonnistui</string>\n    <string name=\"install_to_user\">Asenna käyttäjälle %s</string>\n    <string name=\"install_to_user_message\">Haluatko asentaa %1$s käyttäjälle %2$s? On suositeltavaa asentaa manuaalisesti, pakottaa asennus LSPosedin kautta voi aiheuttaa ongelmia.</string>\n    <string name=\"expand\">laajenna</string>\n    <string name=\"collapse\">pienennä</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Uudelleenoptimoi</string>\n    <string name=\"compile_speed_msg\">Optimoidaan…</string>\n    <string name=\"compile_done\">Optimointi valmis</string>\n    <string name=\"app_launch\">Käynnistä se</string>\n    <string name=\"compile_failed\">Optimointi epäonnistui: palautusarvo on tyhjä</string>\n    <string name=\"compile_failed_with_info\">Optimointi epäonnistui: </string>\n    <string name=\"sort_by_name\">Sovelluksen nimi</string>\n    <string name=\"sort_by_package_name\">Paketin nimi</string>\n    <string name=\"sort_by_install_time\">Asenna aika</string>\n    <string name=\"sort_by_update_time\">Päivityksen aika</string>\n    <string name=\"sort_reverse\">Käänteinen</string>\n    <string name=\"menu_show_system_apps\">Järjestelmäsovellukset</string>\n    <string name=\"menu_sort\">Lajittelu</string>\n    <string name=\"enable_module\">Ota moduuli käyttöön</string>\n    <string name=\"no_scope_selected\">Et valinnut yhtään sovellusta. Jatketaanko?</string>\n    <string name=\"menu_show_games\">Pelit</string>\n    <string name=\"menu_show_modules\">Moduulit</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Valmistelulistan tallentaminen epäonnistui</string>\n    <string name=\"app_version\">Versio: %1$s</string>\n    <string name=\"use_recommended\">Suositeltu</string>\n    <string name=\"no_scope_selected_has_recommended\">Et valinnut yhtään sovellusta. Valitse suositellut sovellukset?</string>\n    <string name=\"use_recommended_message\">Valitse suositellut sovellukset?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed moduuli ei ole vielä aktivoitu</string>\n    <string name=\"requested_by_module\">Suositeltu</string>\n    <string name=\"update_available\">Päivitys saatavilla: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Moduuli %s on poistettu käytöstä koska sovellusta ei ole valittu.</string>\n    <string name=\"android_framework\">Järjestelmän Puitteet</string>\n    <string name=\"menu_backup_and_restore\">Varmuuskopio</string>\n    <string name=\"menu_backup\">Varmuuskopio</string>\n    <string name=\"menu_restore\">Palauta</string>\n    <string name=\"force_stop\">Pakota lopetus</string>\n    <string name=\"force_stop_dlg_title\">Pakotetaanko lopetus?</string>\n    <string name=\"force_stop_dlg_text\">Jos pakotat sovelluksen pysähtymään, se saattaa käyttäytyä väärin.</string>\n    <string name=\"reboot_required\">Uudelleenkäynnistys vaaditaan tämän muutoksen käyttöönottamiseksi</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Piilota</string>\n    <string name=\"deny_list\">%s on denylistissä. Se ei ehkä tule voimaan.</string>\n    <string name=\"deny_list_info\">On denylist</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Näytä toisessa sovelluksessa</string>\n    <string name=\"module_app_info\">Sovelluksen tiedot</string>\n    <string name=\"list_empty\">¶ \\\\\\\\_(konferenssissa)_\\/ ¶\\nEi mitään tässä</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Sanallisten lokien poistaminen käytöstä</string>\n    <string name=\"settings_disable_verbose_log_summary\">Raportti pyytää sisällyttämään sanalliset lokit</string>\n    <string name=\"pure_black_dark_theme\">Musta tumma teema</string>\n    <string name=\"pure_black_dark_theme_summary\">Käytä puhdas musta teema, jos tumma teema on käytössä</string>\n    <string name=\"settings_group_theme\">Teema</string>\n    <string name=\"settings_backup_and_restore\">Varmuuskopioi ja palauta</string>\n    <string name=\"settings_backup_summery\">Varmuuskopioi moduulien listat ja sisällysluettelot.</string>\n    <string name=\"settings_restore_summery\">Palauta moduulien luettelo ja sisällysluettelot.</string>\n    <string name=\"settings_backup\">Varmuuskopio</string>\n    <string name=\"settings_backup_failed2\">Varmuuskopiointi epäonnistui:\\n%s</string>\n    <string name=\"enable_documentui\">Ota DocumentUI käyttöön</string>\n    <string name=\"settings_restore\">Palauta</string>\n    <string name=\"settings_restore_failed2\">Palautus epäonnistui:\\n%s</string>\n    <string name=\"group_network\">Verkko</string>\n    <string name=\"dns_over_http\">DNS yli HTTPS</string>\n    <string name=\"dns_over_http_summary\">Workaround DNS myrkytys joissakin kansoissa</string>\n    <string name=\"theme_color\">Teeman väri</string>\n    <string name=\"theme_color_system\">Järjestelmän teeman väri</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Pakota sovellukset näyttämään käynnistimen kuvakkeet</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10:n jälkeen sovellukset eivät saa piilottaa niiden käynnistyskuvakkeita. Poista valinta käytöstä poistaaksesi järjestelmän ominaisuuden.</string>\n    <string name=\"settings_group_system\">Järjestelmä</string>\n    <string name=\"settings_language\">Kieli</string>\n    <string name=\"settings_translation_contributors\">Käännöksen osallistujat</string>\n    <string name=\"settings_translation\">Osallistu käännökseen</string>\n    <string name=\"settings_translation_summary\">Auta meitä kääntämään %s kielellesi</string>\n    <string name=\"settings_create_shortcut_summary\">Luo pikakuvake, joka voi avata loishallintaohjelman.</string>\n    <string name=\"settings_shortcut_pinned_hint\">Pikakuvake kiinnitetty</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Nykyinen oletuskäynnistin ei tue pin-pikakuvakkeita.</string>\n    <string name=\"settings_enable_status_notification\">Tilailmoitus</string>\n    <string name=\"settings_enable_status_notification_summary\">Näytä ilmoitus, joka voi avata loishallintaohjelman</string>\n    <string name=\"disable_status_notification_error\">Ei pikakuvaketta, ilmoitusta ei voi poistaa käytöstä</string>\n    <string name=\"settings_update_channel\">Päivitä kanava</string>\n    <string name=\"update_channel_stable\">Vakaa</string>\n    <string name=\"update_channel_bate\">Beeta</string>\n    <string name=\"update_channel_nightly\">Yöllinen rakentaminen</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposedin API-kutsujen suojaus</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Estetään dynaamisesti ladatun moduulin koodin käyttö Xposed API:n avulla, mikä saattaa rikkoa joitakin moduuleja mutta hyödyttää turvallisuutta.</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Luennot</string>\n    <string name=\"module_releases\">Julkaisut</string>\n    <string name=\"module_information\">Tiedot</string>\n    <string name=\"module_information_homepage\">Kotisivu</string>\n    <string name=\"module_information_source_url\">Lähdekoodi</string>\n    <string name=\"module_information_collaborators\">Yhteistyökumppanit</string>\n    <string name=\"module_release_view_assets\">Laitteet</string>\n    <string name=\"menu_open_in_browser\">Avaa selaimessa</string>\n    <string name=\"module_release_load_more\">Näytä vanhemmat versiot</string>\n    <string name=\"module_release_no_more\">Ei enää versiota</string>\n    <string name=\"repo_load_failed\">Ei voitu ladata moduulia repo: %s</string>\n    <string name=\"sort_upgradable_first\">Päivitettävissä ensin</string>\n    <string name=\"installed\">Asennettu</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d lataa</item>\n        <item quantity=\"other\">%d lataukset</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Punainen</string>\n    <string name=\"color_pink\">Pinkki</string>\n    <string name=\"color_purple\">Violetti</string>\n    <string name=\"color_deep_purple\">Syvä violetti</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Sininen</string>\n    <string name=\"color_light_blue\">Vaalea sininen</string>\n    <string name=\"color_cyan\">Syaani</string>\n    <string name=\"color_teal\">Sinappi</string>\n    <string name=\"color_green\">Vihreä</string>\n    <string name=\"color_light_green\">Vaalea vihreä</string>\n    <string name=\"color_lime\">Limea</string>\n    <string name=\"color_yellow\">Keltainen</string>\n    <string name=\"color_amber\">Meripihka</string>\n    <string name=\"color_orange\">Oranssi</string>\n    <string name=\"color_deep_orange\">Syvä oranssi</string>\n    <string name=\"color_brown\">Ruskea</string>\n    <string name=\"color_blue_grey\">Sininen harmaa</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Aperçu</string>\n    <string name=\"Modules\">Modules</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d module actif</item>\n        <item quantity=\"other\">%d modules actifs</item>\n    </plurals>\n    <string name=\"Logs\">Journaux</string>\n    <string name=\"Settings\">Réglages</string>\n    <string name=\"feedback_or_suggestion\">Réaction ou suggestion</string>\n    <string name=\"About\">À propos</string>\n    <string name=\"report_issue\">Signaler un problème</string>\n    <string name=\"module_repo\">Dépôt</string>\n    <string name=\"module_repo_up_to_date\">Tous les modules sont à jour</string>\n    <string name=\"module_repo_published_time\">Publié le %s</string>\n    <string name=\"module_repo_updated_time\">Mise à jour le %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modules évolutifs</item>\n        <item quantity=\"other\">%d modules évolutifs</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Voir le code source sur %1$s<br/>Rejoindre notre canal %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">https://github.com/xerta555\nhttps://github.com/tclement0922\nJingMatrix</string>\n    <string name=\"install\">Installer</string>\n    <string name=\"install_summary\">Appuyer pour installer LSPosed</string>\n    <string name=\"not_installed\">Non installé</string>\n    <string name=\"not_install_summary\">LSPosed n\\'est pas installé</string>\n    <string name=\"activated\">Activé</string>\n    <string name=\"partial_activated\">Partiellement activé</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy n\\’est pas chargé correctement</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Certaines fonctions ne fonctionneront pas.<br/>Merci de ne pas remonter celà vers le développeur <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Échec de l\\’injection du sous système</string>\n    <string name=\"system_inject_fail\"><![CDATA[Ceci est rare et peut être causé par <b>Magisk</b> ou certains modules Magisk de basse qualité.<br/>Essayez de désactiver les modules Magisk autres que Riru et LSPosed ou envoyez le journal complet aux développeurs.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Propriétés système incorrectes</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Certaines propriétés nécessaires au système ont été supprimées ou modifiées.<br/>Des modules peuvent s\\'invalider occasionnellement.]]></string>\n    <string name=\"need_update\">Mise à jour nécessaire</string>\n    <string name=\"please_update_summary\">Merci d\\’installer la dernière version de LSPosed</string>\n    <string name=\"developer_warning_title\">Conseils pour les développeurs de modules</string>\n    <string name=\"developer_warning_summary\">Veuillez désactiver les optimisations de déploiement sur Android Studio, ou utilisez la commande `gradlew installDebug` pour installer. Sinon, l\\'APK du module ne sera pas mis à jour.</string>\n    <string name=\"info_api_version\">Version de l\\’API</string>\n    <string name=\"info_framework_version\">Version du framework</string>\n    <string name=\"info_manager_package_name\">Nom de paquet du gestionnaire</string>\n    <string name=\"info_system_version\">Version du système</string>\n    <string name=\"info_device\">Périphérique</string>\n    <string name=\"info_system_abi\">Architecture du système</string>\n    <string name=\"info_dex2oat_wrapper\">Enveloppeur Dex Optimizer</string>\n    <string name=\"enabled\">Activé</string>\n    <string name=\"not_enabled\">Non actif</string>\n    <string name=\"supported\">Supporté</string>\n    <string name=\"unsupported\">Non supporté</string>\n    <string name=\"android_version_unsatisfied\">Version d\\'Android non satisfaisante</string>\n    <string name=\"crashed\">Planté</string>\n    <string name=\"mount_failed\">Échec du montage</string>\n    <string name=\"selinux_permissive\">SELinux est permissif</string>\n    <string name=\"sepolicy_incorrect\">La politique SELinux est incorrecte</string>\n    <string name=\"update_lsposed\">Mettre à jour LSPosed</string>\n    <string name=\"update_lsposed_msg\">Vous confirmez la mise à jour LSPosed ? Ce périphérique redémarrera après la mise à jour effectuée</string>\n    <string name=\"info_copied\">Copié dans le presse-papier</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Bienvenue dans LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Vous utilisez le gestionnaire parasité, qui ne peut pas créer de raccourcis ou même être ouvert à partir d\\'une notification.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Vous utilisez le gestionnaire parasité, qui peut être ouvert depuis la notification.</string>\n    <string name=\"create_shortcut\">Créer le raccourci</string>\n    <string name=\"never_show\">Ne jamais afficher</string>\n    <string name=\"app_welcome\">Gestionnaire parasité recommandé</string>\n    <string name=\"app_welcome_summary\">LSPosed supporte maintenant la parasitage du système afin d\\'éviter les détection, vous pouvez l\\'ouvrir depuis la notification. Il est recommandé de désinstaller l\\'application actuelle.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Sauvegarder</string>\n    <string name=\"nav_item_logs_verbose\">Journaux détaillés</string>\n    <string name=\"nav_item_logs_module\">Journaux des modules</string>\n    <string name=\"logs_saving\">Enregistrement du journal, veuillez patienter</string>\n    <string name=\"logs_saved\">Journaux enregistrés</string>\n    <string name=\"logs_save_failed2\">Échec de la sauvegarde :\\n%s</string>\n    <string name=\"menuClearLog\">Effacer le journal maintenant</string>\n    <string name=\"logs_cleared\">Journal effacé avec succès.</string>\n    <string name=\"scroll_top\">Haut de page</string>\n    <string name=\"loading\">Chargement…</string>\n    <string name=\"scroll_bottom\">Pied de page</string>\n    <string name=\"menuReload\">Recharger</string>\n    <string name=\"logs_clear_failed_2\">Échec de l\\'effacement du journal</string>\n    <string name=\"menu_enable_word_wrap\">Retour à la ligne</string>\n    <string name=\"enabled_verbose_log\">Journaux détaillés activés</string>\n    <string name=\"disabled_verbose_log\">Journaux détaillés désactivés</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(aucune description fournie)</string>\n    <string name=\"warning_xposed_min_version\">Ce module requière une nouvelle version d\\'Xposed (%d) et n\\'a donc pas pu être activé</string>\n    <string name=\"warning_target_version_higher\">Ce module a été conçu pour une nouvelle version d\\’Xposed (%d) et certaines fonctionnalités pourraient ne pas fonctionner</string>\n    <string name=\"no_min_version_specified\">Ce module ne spécifie pas la version d\\'Xposed nécessaire.</string>\n    <string name=\"warning_min_version_too_low\">Ce module à été créé pour la version Xposed %1$d, mais due à des changements incompatibles dans la version %2$d, il à été désactivé</string>\n    <string name=\"warning_installed_on_external_storage\">Ce module ne peut pas être chargé car il est installé sur la carte SD, merci de le déplacer sur le stockage interne</string>\n    <string name=\"module_uninstall\">Désinstaller</string>\n    <string name=\"module_settings\">Réglages du module</string>\n    <string name=\"view_in_repo\">Afficher dans le dépôt</string>\n    <string name=\"module_uninstall_message\">Voulez-vous désinstaller ce module ?</string>\n    <string name=\"module_uninstalled\">Désinstallation de %1$s</string>\n    <string name=\"module_uninstall_failed\">Échec de la désinstallation</string>\n    <string name=\"add_module_to_user\">Ajouter le module à l\\’utilisateur</string>\n    <string name=\"module_installed\">%1$s ajouté à l’utilisateur %2$s</string>\n    <string name=\"module_install_failed\">Échec de l\\’ajout du module</string>\n    <string name=\"install_to_user\">Installer dans l\\'utilisateur %s</string>\n    <string name=\"install_to_user_message\">Vous voulez installer %1$s dans l\\'utilisateur %2$s ? Il est recommandé de l\\'installer manuellement, forcer l\\'installation via LSPosed pourrait causer des problèmes.</string>\n    <string name=\"expand\">développer</string>\n    <string name=\"collapse\">réduire</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Ré-optimiser</string>\n    <string name=\"compile_speed_msg\">Optimisation…</string>\n    <string name=\"compile_done\">Optimisation terminée</string>\n    <string name=\"app_launch\">Démarrer</string>\n    <string name=\"compile_failed\">Échec de l\\’optimisation : la valeur renvoyée est vide</string>\n    <string name=\"compile_failed_with_info\">Échec de l\\’optimisation : </string>\n    <string name=\"sort_by_name\">Trier par nom d\\’application</string>\n    <string name=\"sort_by_package_name\">Trier par nom de paquet</string>\n    <string name=\"sort_by_install_time\">Trier par date d\\’installation</string>\n    <string name=\"sort_by_update_time\">Trier par heure de mise à jour</string>\n    <string name=\"sort_reverse\">Inversé</string>\n    <string name=\"menu_show_system_apps\">Applications système</string>\n    <string name=\"menu_sort\">Trier</string>\n    <string name=\"enable_module\">Activer le module</string>\n    <string name=\"no_scope_selected\">Vous n\\'avez sélectionné aucune application. Continuer ?</string>\n    <string name=\"menu_show_games\">Jeux</string>\n    <string name=\"menu_show_modules\">Modules</string>\n    <string name=\"menu_show_denylist\">Liste de refus</string>\n    <string name=\"failed_to_save_scope_list\">Échec de l\\'enregistrement de la liste des périmètres d\\'applications</string>\n    <string name=\"app_version\">Version : %1$s</string>\n    <string name=\"menu_select\">Choisir</string>\n    <string name=\"use_recommended\">Recommandé</string>\n    <string name=\"no_scope_selected_has_recommended\">Vous n\\'avez sélectionné aucune application. Sélectionner les applications recommandées ?</string>\n    <string name=\"use_recommended_message\">Sélectionner les applications recommandées ?</string>\n    <string name=\"menu_select_all\">Toutes</string>\n    <string name=\"menu_select_none\">Aucune</string>\n    <string name=\"menu_auto_include\">Inclus auto</string>\n    <string name=\"module_is_not_activated_yet\">Le module Xposed n\\’est pas encore activé</string>\n    <string name=\"requested_by_module\">Recommandé</string>\n    <string name=\"update_available\">Mise à jour disponible : %1$s</string>\n    <string name=\"module_disabled_no_selection\">Le module %s a été désactivé étant donné qu\\’aucune application n\\’ai été sélectionné.</string>\n    <string name=\"android_framework\">Cadre du sous-système</string>\n    <string name=\"menu_backup_and_restore\">Sauvegarde</string>\n    <string name=\"menu_backup\">Sauvegarder</string>\n    <string name=\"menu_restore\">Restaurer</string>\n    <string name=\"force_stop\">Forcer l\\’arrêt</string>\n    <string name=\"force_stop_dlg_title\">Forcer l\\'arrêt ?</string>\n    <string name=\"force_stop_dlg_text\">Si vous forcez l\\'arrêt d\\'une application, celle-ci pourrait mal fonctionner.</string>\n    <string name=\"reboot_required\">Un redémarrage est requis pour appliquer les changements</string>\n    <string name=\"reboot\">Redémarrer</string>\n    <string name=\"menu_hide\">Masquage</string>\n    <string name=\"deny_list\">%s est sur la liste de refus. Cela pourrait ne pas avoir d\\'effet.</string>\n    <string name=\"deny_list_info\">Sur liste de refus</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Afficher dans une autre application</string>\n    <string name=\"module_app_info\">Informations d\\’application</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nIl n\\’y a rien ici</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Sous-système</string>\n    <string name=\"settings_disable_verbose_log\">Désactiver les journaux détaillés</string>\n    <string name=\"settings_disable_verbose_log_summary\">Les journaux détaillés sont requis pour signaler des problèmes</string>\n    <string name=\"settings_enable_log_watchdog\">Activer le chien de garde de journal</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Le chien de garde du journal LSPosed modifie les propriétés du système, qui peuvent être exploitées pour détecter LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Thème noir et sombre</string>\n    <string name=\"pure_black_dark_theme_summary\">Utiliser le thème noir pur si le thème noir est activé</string>\n    <string name=\"settings_group_theme\">Thème</string>\n    <string name=\"settings_backup_and_restore\">Sauvegarder et restaurer</string>\n    <string name=\"settings_backup_summery\">Sauvegarder la liste des modules ainsi que leurs champs d\\'applications.</string>\n    <string name=\"settings_restore_summery\">Restaurer la liste des modules ainsi que leurs champs d\\'applications.</string>\n    <string name=\"settings_backup\">Sauvegarder</string>\n    <string name=\"settings_backup_failed2\">Échec de la sauvegarde :\\n%s</string>\n    <string name=\"enable_documentui\">Merci d\\'activer le gestionnaire de fichiers</string>\n    <string name=\"settings_restore\">Restaurer</string>\n    <string name=\"settings_restore_failed2\">Échec de la restauration :\\n%s</string>\n    <string name=\"group_network\">Réseau</string>\n    <string name=\"dns_over_http\">DNS sur HTTPS</string>\n    <string name=\"dns_over_http_summary\">Contourner la censure DNS dans certains pays</string>\n    <string name=\"theme_color\">Couleur du thème</string>\n    <string name=\"theme_color_system\">Couleur d\\'accentuation du système</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Forcer les applications à afficher leurs icônes dans le lanceur</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Après Android 10, les applications ne sont pas autorisées à masquer leurs icônes dans le lanceur. Désactiver ce commutateur pour désactiver cette fonctionnalité du système.</string>\n    <string name=\"settings_group_system\">Système</string>\n    <string name=\"settings_language\">Langage</string>\n    <string name=\"settings_translation_contributors\">Contributeurs de traduction</string>\n    <string name=\"settings_translation\">Participer à la traduction</string>\n    <string name=\"settings_translation_summary\">Aidez-nous à traduire %s dans votre langue</string>\n    <string name=\"settings_create_shortcut_summary\">Créer un raccourci qui peut ouvrir le gestionnaire de parasites</string>\n    <string name=\"settings_shortcut_pinned_hint\">Raccourci épinglé</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Le lanceur par défaut actuel ne supporte pas les raccourcis épinglés</string>\n    <string name=\"settings_enable_status_notification\">Notification d\\'état</string>\n    <string name=\"settings_enable_status_notification_summary\">Afficher une notification qui peut ouvrir le gestionnaire de parasites</string>\n    <string name=\"disable_status_notification_error\">Pas de raccourcis, la notification ne peut pas être désactivée</string>\n    <string name=\"settings_update_channel\">Canal de mise à jour</string>\n    <string name=\"update_channel_stable\">Stable</string>\n    <string name=\"update_channel_bate\">Bêta</string>\n    <string name=\"update_channel_nightly\">Alpha</string>\n    <string name=\"settings_xposed_api_call_protection\">Protection des appels API Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Bloquer dynamiquement le code de module chargé pour utiliser l\\'API Xposed, celà peut casser certains modules mais être bénéfique à la sécurité</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Lisez-moi</string>\n    <string name=\"module_releases\">Versions</string>\n    <string name=\"module_information\">Infos</string>\n    <string name=\"module_information_homepage\">Page d\\’accueil</string>\n    <string name=\"module_information_source_url\">Code source</string>\n    <string name=\"module_information_collaborators\">Collaborateurs</string>\n    <string name=\"module_release_view_assets\">Actifs</string>\n    <string name=\"menu_open_in_browser\">Ouvrir dans le navigateur</string>\n    <string name=\"module_release_load_more\">Afficher les anciennes versions</string>\n    <string name=\"module_release_no_more\">Pas d\\’autres versions</string>\n    <string name=\"repo_load_failed\">Échec de chargement du dépôt des modules : %s</string>\n    <string name=\"sort_upgradable_first\">Évolutifs en premier</string>\n    <string name=\"installed\">installée</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d téléchargé</item>\n        <item quantity=\"other\">%d téléchargés</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rouge</string>\n    <string name=\"color_pink\">Rose</string>\n    <string name=\"color_purple\">Violet</string>\n    <string name=\"color_deep_purple\">Violet foncé</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Bleu</string>\n    <string name=\"color_light_blue\">Bleu clair</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Turquoise</string>\n    <string name=\"color_green\">Vert</string>\n    <string name=\"color_light_green\">Vert clair</string>\n    <string name=\"color_lime\">Vert citron</string>\n    <string name=\"color_yellow\">Jaune</string>\n    <string name=\"color_amber\">Ambre</string>\n    <string name=\"color_orange\">Orange</string>\n    <string name=\"color_deep_orange\">Orange foncé</string>\n    <string name=\"color_brown\">Marron</string>\n    <string name=\"color_blue_grey\">Bleu grisâtre</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">ओवरव्यू</string>\n    <string name=\"Modules\">मॉड्यूल्स</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d मॉड्यूल एनेबल किए गए</item>\n        <item quantity=\"other\">%d मॉड्यूल्स एनेबल किए गए</item>\n    </plurals>\n    <string name=\"Logs\">लॉग्स</string>\n    <string name=\"Settings\">सेटिंग्स</string>\n    <string name=\"feedback_or_suggestion\">फीडबैक या सजेशन</string>\n    <string name=\"About\">इसके बारे में</string>\n    <string name=\"report_issue\">इशू को रिपोर्ट करें</string>\n    <string name=\"module_repo\">रिपोज़िटरी</string>\n    <string name=\"module_repo_up_to_date\">सभी मॉड्यूल अप टू डेट</string>\n    <string name=\"module_repo_published_time\">%s. पर प्रकाशित</string>\n    <string name=\"module_repo_updated_time\">%s. पर अपडेट किया गया</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d मॉड्यूल अपग्रेड करने योग्य</item>\n        <item quantity=\"other\">%d मॉड्यूल अपग्रेड करने योग्य</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[%1$s<br/>पर सोर्स कोड देखें हमारे %2$s चैनल से जुड़ें]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Ahmad Shaikh</string>\n    <string name=\"install\">स्थापित करना</string>\n    <string name=\"install_summary\">LSPosed स्थापित करने के लिए टैप करें</string>\n    <string name=\"not_installed\">स्थापित नहीं हे</string>\n    <string name=\"not_install_summary\">LSPosed स्थापित नहीं है</string>\n    <string name=\"activated\">सक्रिय</string>\n    <string name=\"partial_activated\">आंशिक रूप से सक्रिय</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy ठीक से लोड नहीं है</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[कुछ सुविधाएँ काम नहीं करेंगी।<br/>कृपया इसकी सूचना <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>मैजिक</b></a> डेवलपर को दें।]]></string>\n    <string name=\"system_inject_fail_summary\">सिस्टम फ्रेमवर्क इंजेक्शन विफल</string>\n    <string name=\"system_inject_fail\"><![CDATA[यह दुर्लभ है और <b>Magisk</b> या कुछ निम्न-गुणवत्ता वाले Magisk मॉड्यूल के कारण हो सकता है।<br/>कृपया Riru और LSPosed के अलावा अन्य Magisk मॉड्यूल को अक्षम करने का प्रयास करें या डेवलपर्स को पूर्ण लॉग सबमिट करें।]]></string>\n    <string name=\"system_prop_incorrect_summary\">सिस्टम प्रोप गलत</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[कुछ आवश्यक सिस्टम गुण हटा दिए गए या संशोधित किए गए।<br/>मॉड्यूल कभी-कभी अमान्य हो सकते हैं।]]></string>\n    <string name=\"need_update\">अद्यतन करने की आवश्यकता है</string>\n    <string name=\"please_update_summary\">कृपया LSPosed का नवीनतम संस्करण स्थापित करें</string>\n    <string name=\"info_api_version\">एपीआई संस्करण</string>\n    <string name=\"info_framework_version\">फ्रेमवर्क संस्करण</string>\n    <string name=\"info_manager_package_name\">प्रबंधक पैकेज का नाम</string>\n    <string name=\"info_system_version\">सिस्टम संस्करण</string>\n    <string name=\"info_device\">उपकरण</string>\n    <string name=\"info_system_abi\">सिस्टम एबीआई</string>\n    <string name=\"info_dex2oat_wrapper\">डेक्स ऑप्टिमाइज़र रैपर</string>\n    <string name=\"enabled\">सक्रिय</string>\n    <string name=\"not_enabled\">निष्क्रिय</string>\n    <string name=\"supported\">समर्थित</string>\n    <string name=\"unsupported\">असमर्थित</string>\n    <string name=\"android_version_unsatisfied\">Android संस्करण असंतुष्ट</string>\n    <string name=\"crashed\">दुर्घटनाग्रस्त</string>\n    <string name=\"mount_failed\">माउंट विफल</string>\n    <string name=\"selinux_permissive\">SELinux अनुमेय है</string>\n    <string name=\"sepolicy_incorrect\">SELinux नीति गलत है</string>\n    <string name=\"update_lsposed\">अद्यतन LSPosed</string>\n    <string name=\"update_lsposed_msg\">LSPosed को अपडेट करने की पुष्टि करें? अपडेट पूरा होने के बाद यह डिवाइस रीबूट हो जाएगा</string>\n    <string name=\"info_copied\">क्लिपबोर्ड पर नकल</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPosed में आपका स्वागत है</string>\n    <string name=\"parasitic_welcome_summary\">आप परजीवी प्रबंधक का उपयोग कर रहे हैं, जो शॉर्टकट बना सकता है या सूचना से अभी भी खुला हो सकता है।</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">आप परजीवी प्रबंधक का उपयोग कर रहे हैं, जो सूचना से खुल सकता है।</string>\n    <string name=\"create_shortcut\">शॉर्टकट बनाएं</string>\n    <string name=\"never_show\">कभी भी न दिखाओ</string>\n    <string name=\"app_welcome\">परजीवी प्रबंधक की सिफारिश की</string>\n    <string name=\"app_welcome_summary\">LSPosed अब पता लगाने से बचने के लिए सिस्टम परजीवीकरण का समर्थन करता है, आप अधिसूचना से परजीवी प्रबंधक खोल सकते हैं। वर्तमान एप्लिकेशन को अनइंस्टॉल करने की अनुशंसा की जाती है।</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">बचाना</string>\n    <string name=\"nav_item_logs_verbose\">वर्बोज़ लॉग्स</string>\n    <string name=\"nav_item_logs_module\">मॉड्यूल लॉग</string>\n    <string name=\"logs_saving\">लॉग सहेजा जा रहा है, कृपया प्रतीक्षा करें</string>\n    <string name=\"logs_saved\">लॉग सेव हो गए</string>\n    <string name=\"logs_save_failed2\">सहेजने में विफल:\\n%s</string>\n    <string name=\"menuClearLog\">अभी लॉग साफ़ करें</string>\n    <string name=\"logs_cleared\">लॉग सफलतापूर्वक साफ़ किया गया।</string>\n    <string name=\"scroll_top\">शीर्ष तक स्क्रॉल करें</string>\n    <string name=\"loading\">लोड हो रहा है…</string>\n    <string name=\"scroll_bottom\">नीचे स्क्रॉल करें</string>\n    <string name=\"menuReload\">पुनः लोड करें</string>\n    <string name=\"logs_clear_failed_2\">लॉग साफ़ करने में विफल</string>\n    <string name=\"menu_enable_word_wrap\">वर्ड रैप</string>\n    <string name=\"enabled_verbose_log\">वर्बोज़ लॉग सक्षम</string>\n    <string name=\"disabled_verbose_log\">वर्बोज़ लॉग अक्षम</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(कोई विवरण नहीं दिया गया)</string>\n    <string name=\"warning_xposed_min_version\">इस मॉड्यूल को एक नए Xposed संस्करण (%d) की आवश्यकता है और इस प्रकार इसे सक्रिय नहीं किया जा सकता है</string>\n    <string name=\"warning_target_version_higher\">यह मॉड्यूल एक नए Xposed संस्करण (%d) के लिए डिज़ाइन किया गया है और इस प्रकार कुछ कार्यात्मकताएँ काम नहीं कर सकती हैं</string>\n    <string name=\"no_min_version_specified\">यह मॉड्यूल Xposed संस्करण को निर्दिष्ट नहीं करता है जिसकी उसे आवश्यकता है।</string>\n    <string name=\"warning_min_version_too_low\">यह मॉड्यूल Xposed संस्करण %1$dके लिए बनाया गया था, लेकिन संस्करण %2$dमें असंगत परिवर्तनों के कारण, इसे अक्षम कर दिया गया है</string>\n    <string name=\"warning_installed_on_external_storage\">यह मॉड्यूल लोड नहीं किया जा सकता क्योंकि यह एसडी कार्ड पर स्थापित है, कृपया इसे आंतरिक भंडारण में ले जाएं</string>\n    <string name=\"module_uninstall\">स्थापना रद्द करें</string>\n    <string name=\"module_settings\">मॉड्यूल सेटिंग्स</string>\n    <string name=\"view_in_repo\">रेपो में देखें</string>\n    <string name=\"module_uninstall_message\">क्या आप इस मॉड्यूल को अनइंस्टॉल करना चाहते हैं?</string>\n    <string name=\"module_uninstalled\">अनइंस्टॉल किया गया %1$s</string>\n    <string name=\"module_uninstall_failed\">अनइंस्टॉल असफल</string>\n    <string name=\"add_module_to_user\">उपयोगकर्ता में मॉड्यूल जोड़ें</string>\n    <string name=\"module_installed\">उपयोगकर्ता %2$sमें %1$s जोड़ा गया</string>\n    <string name=\"module_install_failed\">मॉड्यूल जोड़ना विफल</string>\n    <string name=\"install_to_user\">उपयोगकर्ता को स्थापित करें %s</string>\n    <string name=\"install_to_user_message\">उपयोगकर्ता %2$sपर %1$s स्थापित करना चाहते हैं? मैन्युअल रूप से स्थापित करने की अनुशंसा की जाती है, LSPosed के माध्यम से स्थापना को मजबूर करने से समस्या हो सकती है।</string>\n    <string name=\"expand\">विस्तार</string>\n    <string name=\"collapse\">ढहना</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">पुन: अनुकूलित</string>\n    <string name=\"compile_speed_msg\">अनुकूलन…</string>\n    <string name=\"compile_done\">अनुकूलन पूर्ण</string>\n    <string name=\"app_launch\">इसे लॉन्च करें</string>\n    <string name=\"compile_failed\">अनुकूलन विफल: वापसी मूल्य खाली है</string>\n    <string name=\"compile_failed_with_info\">अनुकूलन विफल: </string>\n    <string name=\"sort_by_name\">आवेदन का नाम</string>\n    <string name=\"sort_by_package_name\">पैकेज का नाम</string>\n    <string name=\"sort_by_install_time\">समय स्थापित करें</string>\n    <string name=\"sort_by_update_time\">समय सुधारें</string>\n    <string name=\"sort_reverse\">उल्टा</string>\n    <string name=\"menu_show_system_apps\">सिस्टम ऐप्स</string>\n    <string name=\"menu_sort\">छंटाई</string>\n    <string name=\"enable_module\">मॉड्यूल सक्षम करें</string>\n    <string name=\"no_scope_selected\">आपने कोई ऐप नहीं चुना है। जारी रखें?</string>\n    <string name=\"menu_show_games\">खेल</string>\n    <string name=\"menu_show_modules\">मॉड्यूल</string>\n    <string name=\"menu_show_denylist\">इनकार करनेवाला</string>\n    <string name=\"failed_to_save_scope_list\">कार्यक्षेत्र सूची सहेजने में विफल</string>\n    <string name=\"app_version\">संस्करण: %1$s</string>\n    <string name=\"use_recommended\">अनुशंसित</string>\n    <string name=\"no_scope_selected_has_recommended\">आपने कोई ऐप नहीं चुना है। अनुशंसित ऐप्स चुनें?</string>\n    <string name=\"use_recommended_message\">अनुशंसित ऐप्स चुनें?</string>\n    <string name=\"module_is_not_activated_yet\">एक्सपोज़ड मॉड्यूल अभी तक सक्रिय नहीं है</string>\n    <string name=\"requested_by_module\">अनुशंसित</string>\n    <string name=\"update_available\">अपडेट उपलब्ध: %1$s</string>\n    <string name=\"module_disabled_no_selection\">मॉड्यूल %s को अक्षम कर दिया गया है क्योंकि कोई ऐप नहीं चुना गया है।</string>\n    <string name=\"android_framework\">सिस्टम फ्रेमवर्क</string>\n    <string name=\"menu_backup_and_restore\">बैकअप</string>\n    <string name=\"menu_backup\">बैकअप</string>\n    <string name=\"menu_restore\">पुनर्स्थापित करना</string>\n    <string name=\"force_stop\">जबर्दस्ती बंद करें</string>\n    <string name=\"force_stop_dlg_title\">जबर्दस्ती बंद करें?</string>\n    <string name=\"force_stop_dlg_text\">यदि आप किसी ऐप को जबरदस्ती बंद करते हैं, तो वह गलत व्यवहार कर सकता है।</string>\n    <string name=\"reboot_required\">इस परिवर्तन को लागू करने के लिए रीबूट की आवश्यकता है</string>\n    <string name=\"reboot\">रीबूट</string>\n    <string name=\"menu_hide\">छिपाना</string>\n    <string name=\"deny_list\">%s अस्वीकृत सूची में है। यह प्रभावी नहीं हो सकता है।</string>\n    <string name=\"deny_list_info\">अस्वीकृत सूची पर</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">अन्य ऐप में देखें</string>\n    <string name=\"module_app_info\">अनुप्रयोग की जानकारी</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nयहाँ कुछ भी नहीं</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">रूपरेखा</string>\n    <string name=\"settings_disable_verbose_log\">वर्बोज़ लॉग अक्षम करें</string>\n    <string name=\"settings_disable_verbose_log_summary\">रिपोर्ट वर्बोज़ लॉग शामिल करने का अनुरोध जारी करती है</string>\n    <string name=\"pure_black_dark_theme\">ब्लैक डार्क थीम</string>\n    <string name=\"pure_black_dark_theme_summary\">यदि डार्क थीम सक्षम है तो शुद्ध काली थीम का उपयोग करें</string>\n    <string name=\"settings_group_theme\">थीम</string>\n    <string name=\"settings_backup_and_restore\">बैकअप और पुनर्स्थापना</string>\n    <string name=\"settings_backup_summery\">बैकअप मॉड्यूल सूची और कार्यक्षेत्र सूचियाँ।</string>\n    <string name=\"settings_restore_summery\">मॉड्यूल सूची और कार्यक्षेत्र सूचियों को पुनर्स्थापित करें।</string>\n    <string name=\"settings_backup\">बैकअप</string>\n    <string name=\"settings_backup_failed2\">बैकअप में विफल:\\n%s</string>\n    <string name=\"enable_documentui\">कृपया DocumentUI सक्षम करें</string>\n    <string name=\"settings_restore\">पुनर्स्थापित करना</string>\n    <string name=\"settings_restore_failed2\">पुनर्स्थापित करने में विफल:\\n%s</string>\n    <string name=\"group_network\">नेटवर्क</string>\n    <string name=\"dns_over_http\">एचटीटीपीएस पर डीएनएस</string>\n    <string name=\"dns_over_http_summary\">कुछ देशों में DNS विषाक्तता का समाधान</string>\n    <string name=\"theme_color\">थीम रंग</string>\n    <string name=\"theme_color_system\">सिस्टम थीम रंग</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">लॉन्चर आइकन दिखाने के लिए ऐप्स को बाध्य करें</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10 के बाद, ऐप्स को अपने लॉन्चर आइकन छिपाने की अनुमति नहीं है। इस सिस्टम सुविधा को अक्षम करने के लिए टॉगल बंद करें।</string>\n    <string name=\"settings_group_system\">प्रणाली</string>\n    <string name=\"settings_language\">भाषा</string>\n    <string name=\"settings_translation_contributors\">अनुवाद योगदानकर्ता</string>\n    <string name=\"settings_translation\">अनुवाद में भाग लें</string>\n    <string name=\"settings_translation_summary\">%s को अपनी भाषा में अनुवाद करने में हमारी सहायता करें</string>\n    <string name=\"settings_create_shortcut_summary\">एक शॉर्टकट बनाएं जो परजीवी प्रबंधक खोल सके</string>\n    <string name=\"settings_shortcut_pinned_hint\">शॉर्टकट पिन किया गया</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">वर्तमान डिफ़ॉल्ट लांचर पिन शॉर्टकट का समर्थन नहीं करता</string>\n    <string name=\"settings_enable_status_notification\">स्थिति अधिसूचना</string>\n    <string name=\"settings_enable_status_notification_summary\">एक अधिसूचना दिखाएं जो परजीवी प्रबंधक खोल सकती है</string>\n    <string name=\"disable_status_notification_error\">कोई शॉर्टकट नहीं, अधिसूचना अक्षम नहीं कर सकता</string>\n    <string name=\"settings_update_channel\">चैनल अपडेट करें</string>\n    <string name=\"update_channel_stable\">स्थिर</string>\n    <string name=\"update_channel_bate\">बीटा</string>\n    <string name=\"update_channel_nightly\">सॉफ़्टवेयर की स्थिरता</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API कॉल सुरक्षा</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Xposed API का उपयोग करने के लिए गतिशील रूप से लोड किए गए मॉड्यूल कोड को ब्लॉक करें, इससे कुछ मॉड्यूल टूट सकते हैं लेकिन सुरक्षा को लाभ होगा</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">रीडमी</string>\n    <string name=\"module_releases\">विज्ञप्ति</string>\n    <string name=\"module_information\">जानकारी</string>\n    <string name=\"module_information_homepage\">होमपेज</string>\n    <string name=\"module_information_source_url\">सोर्स कोड</string>\n    <string name=\"module_information_collaborators\">सहयोगियों</string>\n    <string name=\"module_release_view_assets\">संपत्तियां</string>\n    <string name=\"menu_open_in_browser\">ब्राउज़र में खोलें</string>\n    <string name=\"module_release_load_more\">पुराने संस्करण दिखाएं</string>\n    <string name=\"module_release_no_more\">कोई और रिलीज नहीं</string>\n    <string name=\"repo_load_failed\">मॉड्यूल रेपो लोड करने में विफल: %s</string>\n    <string name=\"sort_upgradable_first\">पहले अपग्रेड करने योग्य</string>\n    <string name=\"installed\">स्थापित</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d डाउनलोड</item>\n        <item quantity=\"other\">%d डाउनलोड</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">सकुरा</string>\n    <string name=\"color_red\">लाल</string>\n    <string name=\"color_pink\">गुलाबी</string>\n    <string name=\"color_purple\">बैंगनी</string>\n    <string name=\"color_deep_purple\">गहरा बैंगनी</string>\n    <string name=\"color_indigo\">नील</string>\n    <string name=\"color_blue\">नीला</string>\n    <string name=\"color_light_blue\">हल्का नीला रंग</string>\n    <string name=\"color_cyan\">सियान</string>\n    <string name=\"color_teal\">टील</string>\n    <string name=\"color_green\">हरा</string>\n    <string name=\"color_light_green\">हल्का हरा</string>\n    <string name=\"color_lime\">नींबू</string>\n    <string name=\"color_yellow\">पीला</string>\n    <string name=\"color_amber\">अंबर</string>\n    <string name=\"color_orange\">संतरा</string>\n    <string name=\"color_deep_orange\">गहरा नारंगी</string>\n    <string name=\"color_brown\">भूरा</string>\n    <string name=\"color_blue_grey\">नीला ग्रे</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Pregled</string>\n    <string name=\"Modules\">Moduli</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul omogućen</item>\n        <item quantity=\"few\">%d modula omogućeno</item>\n        <item quantity=\"other\">%d modula omogućeno</item>\n    </plurals>\n    <string name=\"Logs\">Zapisi</string>\n    <string name=\"Settings\">Postavke</string>\n    <string name=\"feedback_or_suggestion\">Povratna informacija ili prijedlog</string>\n    <string name=\"About\">Informacije</string>\n    <string name=\"report_issue\">Prijavi problem</string>\n    <string name=\"module_repo\">Spremište modula</string>\n    <string name=\"module_repo_up_to_date\">Svi moduli ažurirani</string>\n    <string name=\"module_repo_published_time\">Objavljeno u %s</string>\n    <string name=\"module_repo_updated_time\">Ažurirano u %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul moguće nadograditi</item>\n        <item quantity=\"few\">%d modula moguće nadograditi</item>\n        <item quantity=\"other\">%d modula moguće nadograditi</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Pogledajte izvorni kod na %1$s<br/>Pridružite se našem %2$s kanalu]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">https://github.com/cube2412</string>\n    <string name=\"install\">Instaliraj</string>\n    <string name=\"install_summary\">Dodirnite za instaliranje LSPosed</string>\n    <string name=\"not_installed\">Nije instalirano</string>\n    <string name=\"not_install_summary\">LSPosed nije instaliran</string>\n    <string name=\"activated\">Aktiviran</string>\n    <string name=\"partial_activated\">Djelomično aktiviran</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy nije pravilno učitan</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Neke značajke neće raditi.<br/>Prijavite ovo <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> programeru.]]></string>\n    <string name=\"system_inject_fail_summary\">Injektiranje u System Framework nije uspjelo</string>\n    <string name=\"system_inject_fail\"><![CDATA[Ovo je rijetko i može biti uzrokovano aplikacijom <b>Magisk</b> ili nekim Magisk modulima niske kvalitete.<br/>Pokušajte onemogućiti Magisk module koji nisu Riru i LSPosed ili pošaljite cijeli zapis programerima.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Svojstva sustava nisu ispravna</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Neka od potrebnih svojstva sustava su izbrisana ili izmijenjena.<br/>Moduli povremeno mogu biti nedostupni.]]></string>\n    <string name=\"need_update\">Treba ažurirati</string>\n    <string name=\"please_update_summary\">Molimo instalirajte najnoviju verziju LSPosed</string>\n    <string name=\"info_api_version\">API verzija</string>\n    <string name=\"info_framework_version\">Framework verzija</string>\n    <string name=\"info_manager_package_name\">Naziv paketa upravitelja</string>\n    <string name=\"info_system_version\">System verzija</string>\n    <string name=\"info_device\">Uređaj</string>\n    <string name=\"info_system_abi\">System ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Omogućeno</string>\n    <string name=\"not_enabled\">Nije omogućeno</string>\n    <string name=\"supported\">Podržano</string>\n    <string name=\"unsupported\">Nepodržano</string>\n    <string name=\"android_version_unsatisfied\">Verzija Androida nije zadovoljavajuća</string>\n    <string name=\"crashed\">Srušio se</string>\n    <string name=\"mount_failed\">Postavljanje nije uspjelo</string>\n    <string name=\"selinux_permissive\">SELinux je permisivan</string>\n    <string name=\"sepolicy_incorrect\">Pravila SELinuxa je netočna</string>\n    <string name=\"update_lsposed\">Ažurirajte LSPosed</string>\n    <string name=\"update_lsposed_msg\">Potvrditi ažuriranje LSPosed? Ovaj će se uređaj ponovno pokrenuti nakon završetka ažuriranja</string>\n    <string name=\"info_copied\">Kopirano u međuspremnik</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Dobrodošli u LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Koristite parazitski upravitelj, koji može stvoriti prečac ili još uvijek otvoriti iz obavijesti.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Koristite parazitski upravitelj koji se može otvoriti iz obavijesti.</string>\n    <string name=\"create_shortcut\">Napravi prečac</string>\n    <string name=\"never_show\">Nikad ne pokazuj</string>\n    <string name=\"app_welcome\">Parazitski Manager Preporučen</string>\n    <string name=\"app_welcome_summary\">LSPosed sada podržava parazitizaciju sustava kako bi se izbjeglo otkrivanje, možete otvoriti parazitski upravitelj iz obavijesti. Preporuča se deinstalirati trenutnu aplikaciju.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Sačuvaj</string>\n    <string name=\"nav_item_logs_verbose\">Opširni zapisi</string>\n    <string name=\"nav_item_logs_module\">Zapisi Modula</string>\n    <string name=\"logs_saving\">Spremanje dnevnika, pričekajte</string>\n    <string name=\"logs_saved\">Zapisi spremljeni</string>\n    <string name=\"logs_save_failed2\">Neuspješno spremanje:\\n%s</string>\n    <string name=\"menuClearLog\">Očisti zapis sada</string>\n    <string name=\"logs_cleared\">Zapis je uspješno izbrisan.</string>\n    <string name=\"scroll_top\">Pomaknite se na vrh</string>\n    <string name=\"loading\">Učitavanje…</string>\n    <string name=\"scroll_bottom\">Pomaknite se do dna</string>\n    <string name=\"menuReload\">Ponovno učitaj</string>\n    <string name=\"logs_clear_failed_2\">Brisanje zapisa nije uspjelo</string>\n    <string name=\"menu_enable_word_wrap\">Prijelom riječi</string>\n    <string name=\"enabled_verbose_log\">Opširni zapis omogućen</string>\n    <string name=\"disabled_verbose_log\">Opširni zapis onemogućen</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(nema opisa)</string>\n    <string name=\"warning_xposed_min_version\">Ovaj modul zahtijeva noviju verziju Xposed (%d) i stoga se ne može aktivirati</string>\n    <string name=\"warning_target_version_higher\">Ovaj modul je dizajniran za noviju verziju Xposed (%d) i stoga neke funkcije možda neće raditi</string>\n    <string name=\"no_min_version_specified\">Ovaj modul ne navodi verziju Xposed koja mu je potrebna.</string>\n    <string name=\"warning_min_version_too_low\">Ovaj modul je stvoren za Xposed verziju %1$d, zbog nekompatibilnih promjena u verziji %2$d, modul je onemogućen</string>\n    <string name=\"warning_installed_on_external_storage\">Ovaj modul nije moguće učitati jer je instaliran na SD kartici, molimo premjestite ga u internu memoriju</string>\n    <string name=\"module_uninstall\">Deinstaliraj</string>\n    <string name=\"module_settings\">Postavke modula</string>\n    <string name=\"view_in_repo\">Pogledaj u Repou</string>\n    <string name=\"module_uninstall_message\">Želite li deinstalirati ovaj modul?</string>\n    <string name=\"module_uninstalled\">Deinstalirano %1$s</string>\n    <string name=\"module_uninstall_failed\">Deinstalacija nije uspjela</string>\n    <string name=\"add_module_to_user\">Dodavanje modula korisniku</string>\n    <string name=\"module_installed\">Dodano %1$s korisniku %2$s</string>\n    <string name=\"module_install_failed\">Dodavanje modula nije uspjelo</string>\n    <string name=\"install_to_user\">Instaliraj na korisnika %s</string>\n    <string name=\"install_to_user_message\">Želite li instalirati %1$s korisniku %2$s? Preporuča se ručna instalacija, prisilna instalacija putem LSPoseda može uzrokovati probleme.</string>\n    <string name=\"expand\">proširi</string>\n    <string name=\"collapse\">sklopi</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Ponovno optimiziraj</string>\n    <string name=\"compile_speed_msg\">Optimizacija…</string>\n    <string name=\"compile_done\">Optimizacija dovršena</string>\n    <string name=\"app_launch\">Pokreni ga</string>\n    <string name=\"compile_failed\">Optimizacija nije uspjela: povratna vrijednost je prazna</string>\n    <string name=\"compile_failed_with_info\">Optimizacija nije uspjela: </string>\n    <string name=\"sort_by_name\">Naziv aplikacije</string>\n    <string name=\"sort_by_package_name\">Naziv paketa</string>\n    <string name=\"sort_by_install_time\">Vrijeme instalacije</string>\n    <string name=\"sort_by_update_time\">Vrijeme ažuriranja</string>\n    <string name=\"sort_reverse\">Obrnuto</string>\n    <string name=\"menu_show_system_apps\">Aplikacije sustava</string>\n    <string name=\"menu_sort\">Sortiranje</string>\n    <string name=\"enable_module\">Omogući modul</string>\n    <string name=\"no_scope_selected\">Niste odabrali nijednu aplikaciju. Nastaviti?</string>\n    <string name=\"menu_show_games\">Igre</string>\n    <string name=\"menu_show_modules\">Moduli</string>\n    <string name=\"menu_show_denylist\">Lista zabrane</string>\n    <string name=\"failed_to_save_scope_list\">Spremanje popisa opsega primjene nije uspjelo</string>\n    <string name=\"app_version\">Verzija: %1$s</string>\n    <string name=\"use_recommended\">Preporučeno</string>\n    <string name=\"no_scope_selected_has_recommended\">Niste odabrali nijednu aplikaciju. Odaberi preporučene aplikacije?</string>\n    <string name=\"use_recommended_message\">Odaberi preporučene aplikacije?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modul još nije aktiviran</string>\n    <string name=\"requested_by_module\">Preporučeno</string>\n    <string name=\"update_available\">Dostupno ažuriranje: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s je onemogućen jer nije odabrana nijedna aplikacija.</string>\n    <string name=\"android_framework\">System Framework</string>\n    <string name=\"menu_backup_and_restore\">Sigurnosna kopija</string>\n    <string name=\"menu_backup\">Sigurnosna kopija</string>\n    <string name=\"menu_restore\">Vrati sigurnosnu kopiju</string>\n    <string name=\"force_stop\">Prisilno zaustavi</string>\n    <string name=\"force_stop_dlg_title\">Prisilno zaustaviti?</string>\n    <string name=\"force_stop_dlg_text\">Ako prisilno zaustavite aplikaciju, može doći do nepredvidivog ponašanja.</string>\n    <string name=\"reboot_required\">Za primjenu ove promjene potrebno je ponovno pokretanje</string>\n    <string name=\"reboot\">Ponovno podizanje sustava</string>\n    <string name=\"menu_hide\">Sakrij</string>\n    <string name=\"deny_list\">%s je na listi zabrane. Možda neće imati efekta.</string>\n    <string name=\"deny_list_info\">Na popisu zabrane</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Pogledaj u drugoj aplikaciji</string>\n    <string name=\"module_app_info\">Informacije o aplikaciji</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nOvdje nema ničega</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Onemogući opširne zapise</string>\n    <string name=\"settings_disable_verbose_log_summary\">Izvješće o problemima zahtijeva uključivanje opširnih zapisa</string>\n    <string name=\"pure_black_dark_theme\">Crna tamna tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Koristi čistu crnu temu ako je tamna tema omogućena</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Sigurnosno kopiranje i vraćanje</string>\n    <string name=\"settings_backup_summery\">Lista sigurnosnih kopija modula i opširne liste.</string>\n    <string name=\"settings_restore_summery\">Lista modula vraćenih iz sigurnosne kopije i opsežne liste.</string>\n    <string name=\"settings_backup\">Sigurnosna kopija</string>\n    <string name=\"settings_backup_failed2\">Sigurnosno kopiranje nije uspjelo:\\n%s</string>\n    <string name=\"enable_documentui\">Molimo omogućite DocumentUI</string>\n    <string name=\"settings_restore\">Vrati</string>\n    <string name=\"settings_restore_failed2\">Nije uspjelo vraćanje:\\n%s</string>\n    <string name=\"group_network\">Mreža</string>\n    <string name=\"dns_over_http\">DNS preko HTTPS-a</string>\n    <string name=\"dns_over_http_summary\">Zaobilazno rješenje DNS trovanja u nekim zemljama</string>\n    <string name=\"theme_color\">Boja teme</string>\n    <string name=\"theme_color_system\">Boja teme sustava</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Prisilite aplikacije da prikazuju ikone pokretača</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Nakon Androida 10 aplikacijama nije dopušteno skrivanje ikona pokretača. Isključite prekidač da biste onemogućili ovu značajku sustava.</string>\n    <string name=\"settings_group_system\">Sustav</string>\n    <string name=\"settings_language\">Jezik</string>\n    <string name=\"settings_translation_contributors\">Suradnici prijevoda</string>\n    <string name=\"settings_translation\">Sudjelujte u prevođenju</string>\n    <string name=\"settings_translation_summary\">Pomozite nam prevesti %s na vaš jezik</string>\n    <string name=\"settings_create_shortcut_summary\">Napravite prečac koji može otvoriti parazitski upravitelj</string>\n    <string name=\"settings_shortcut_pinned_hint\">Prečac prikvačen</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Trenutačni zadani pokretač ne podržava prečace pribadače</string>\n    <string name=\"settings_enable_status_notification\">Obavijest o statusu</string>\n    <string name=\"settings_enable_status_notification_summary\">Prikaži obavijest koja može otvoriti parazitski upravitelj</string>\n    <string name=\"disable_status_notification_error\">Nema prečaca, nije moguće onemogućiti obavijest</string>\n    <string name=\"settings_update_channel\">Ažurirajte kanal</string>\n    <string name=\"update_channel_stable\">Stabilan</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Noćna izgradnja</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API zaštita poziva</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokirajte dinamički učitani kod modula za korištenje Xposed API-ja, to može pokvariti neke module, ali doprinosi sigurnosti</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Pročitaj me</string>\n    <string name=\"module_releases\">Izdanja</string>\n    <string name=\"module_information\">Info</string>\n    <string name=\"module_information_homepage\">Početna stranica</string>\n    <string name=\"module_information_source_url\">Izvorni kod</string>\n    <string name=\"module_information_collaborators\">Suradnici</string>\n    <string name=\"module_release_view_assets\">Imovina</string>\n    <string name=\"menu_open_in_browser\">Otvori u pretraživaču</string>\n    <string name=\"module_release_load_more\">Prikaži starije verzije</string>\n    <string name=\"module_release_no_more\">Nema više puštanja</string>\n    <string name=\"repo_load_failed\">Neuspješno učitavanje spremišta modula: %s</string>\n    <string name=\"sort_upgradable_first\">Prvo nadogradivo</string>\n    <string name=\"installed\">instalirano</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d preuzimanje</item>\n        <item quantity=\"few\">%d preuzimanja</item>\n        <item quantity=\"other\">%d preuzimanja</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Crvena</string>\n    <string name=\"color_pink\">Ružičasta</string>\n    <string name=\"color_purple\">Ljubičasta</string>\n    <string name=\"color_deep_purple\">Tamno ljubičasta</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Plava</string>\n    <string name=\"color_light_blue\">Svijetlo plava</string>\n    <string name=\"color_cyan\">cijan</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">zelena</string>\n    <string name=\"color_light_green\">Svijetlo zelena</string>\n    <string name=\"color_lime\">Vapno</string>\n    <string name=\"color_yellow\">Žuta boja</string>\n    <string name=\"color_amber\">jantar</string>\n    <string name=\"color_orange\">naranča</string>\n    <string name=\"color_deep_orange\">Tamno narančasta</string>\n    <string name=\"color_brown\">Smeđa</string>\n    <string name=\"color_blue_grey\">Plavo siva</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Áttekintés</string>\n    <string name=\"Modules\">Modulok</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul aktiválva</item>\n        <item quantity=\"other\">%d modulok aktiválva</item>\n    </plurals>\n    <string name=\"Logs\">Napló fájlok</string>\n    <string name=\"Settings\">Beállítások</string>\n    <string name=\"feedback_or_suggestion\">Visszajelzés vagy javaslat</string>\n    <string name=\"About\">Névjegy</string>\n    <string name=\"report_issue\">Hibabejelentés</string>\n    <string name=\"module_repo\">Modulok</string>\n    <string name=\"module_repo_up_to_date\">Minden modul naprakész</string>\n    <string name=\"module_repo_published_time\">Közzétéve: %s</string>\n    <string name=\"module_repo_updated_time\">Frissítve: %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul frissíthető</item>\n        <item quantity=\"other\">%d modul frissíthető</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[A forráskódot megnézheted itt: %1$s<br/> Iratkozz fel a %2$s csatornánkra]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">عبدو المكحل, Balázs Juhász, Krisztián Molnár</string>\n    <string name=\"install\">Telepítés</string>\n    <string name=\"install_summary\">Koppints az LSPosed telepítéséhez</string>\n    <string name=\"not_installed\">Nincs telepítve</string>\n    <string name=\"not_install_summary\">Az LSPosed nincs telepítve</string>\n    <string name=\"activated\">Aktiválva</string>\n    <string name=\"partial_activated\">Részben aktiválva</string>\n    <string name=\"selinux_policy_not_loaded_summary\">Az SEPolicy nem töltött be megfelelően</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Néhány funkció nem fog működni.<br/>Kérjük, jelezze ezt a <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> fejlesztőnek.]]></string>\n    <string name=\"system_inject_fail_summary\">A System Framework injektálása sikertelen</string>\n    <string name=\"system_inject_fail\"><![CDATA[Ez sajnos gyakori probléma, amit lehet, hogy a\n<b>Magisk</b> vagy néhány Magisk modul okozott.<br/> Kérlek próbálj meg deaktiválni néhány Magisk modult a Riru és LSPosed modulokon kívül vagy küldj el egy teljes napló fájlt a fejlesztőknek.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Helytelen rendszertulajdonságok</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Néhány szükséges rendszertulajdonság törlése vagy módosítása.<br/>A modulok időnként érvénytelenné válhatnak.]]></string>\n    <string name=\"need_update\">Frissítésre van szükség</string>\n    <string name=\"please_update_summary\">Kérjük, telepítse az LSPosed legújabb verzióját</string>\n    <string name=\"info_api_version\">API verzió</string>\n    <string name=\"info_framework_version\">Keretrendszer verzió</string>\n    <string name=\"info_manager_package_name\">Menedzser csomag neve</string>\n    <string name=\"info_system_version\">Rendszer verzió</string>\n    <string name=\"info_device\">Eszköz</string>\n    <string name=\"info_system_abi\">Rendszer ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Engedélyezve</string>\n    <string name=\"not_enabled\">Nincs engedélyezve</string>\n    <string name=\"supported\">Támogatott</string>\n    <string name=\"unsupported\">Nem támogatott</string>\n    <string name=\"android_version_unsatisfied\">Az Android verzió nem megfelelő</string>\n    <string name=\"crashed\">Összeomlott</string>\n    <string name=\"mount_failed\">A csatolás sikertelen</string>\n    <string name=\"selinux_permissive\">Az SELinux engedélyezett</string>\n    <string name=\"sepolicy_incorrect\">Az SELinux házirend helytelen</string>\n    <string name=\"update_lsposed\">Az LSPosed frissítése</string>\n    <string name=\"update_lsposed_msg\">Jóváhagyja az LSPosed frissítését? A készülék a frissítés befejezése után újra fog indulni</string>\n    <string name=\"info_copied\">A vágólapra másolva</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Üdvözöljük az LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Ön használja a parazita menedzser, amely képes létrehozni parancsikont vagy még mindig nyitva értesítésből.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Ön a parazita-kezelőt használja, amely az értesítésből megnyitható.</string>\n    <string name=\"create_shortcut\">Parancsikon létrehozása</string>\n    <string name=\"never_show\">Soha ne mutassa</string>\n    <string name=\"app_welcome\">Parazita menedzser Ajánlott</string>\n    <string name=\"app_welcome_summary\">Az LSPosed mostantól támogatja a rendszerparazitizációt a felismerés elkerülése érdekében, a parazita-kezelőt az értesítésből nyithatja meg. Javasoljuk, hogy távolítsa el az aktuális alkalmazást.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Mentés</string>\n    <string name=\"nav_item_logs_verbose\">Szöveges naplók</string>\n    <string name=\"nav_item_logs_module\">Modulok Naplói</string>\n    <string name=\"logs_saving\">Napló mentése, kérem várjon</string>\n    <string name=\"logs_saved\">Mentett naplók</string>\n    <string name=\"logs_save_failed2\">Nem sikerült menteni:\\n%s</string>\n    <string name=\"menuClearLog\">Törölje a naplót most</string>\n    <string name=\"logs_cleared\">A napló sikeresen törlődött.</string>\n    <string name=\"scroll_top\">Görgessen a tetejére</string>\n    <string name=\"loading\">Betöltés…</string>\n    <string name=\"scroll_bottom\">Görgessen az aljára</string>\n    <string name=\"menuReload\">Újratöltés</string>\n    <string name=\"logs_clear_failed_2\">Nem sikerült törölni a naplót</string>\n    <string name=\"menu_enable_word_wrap\">Word Wrap</string>\n    <string name=\"enabled_verbose_log\">Szöveges napló engedélyezve</string>\n    <string name=\"disabled_verbose_log\">Szöveges napló letiltva</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(nincs leírás megadva)</string>\n    <string name=\"warning_xposed_min_version\">Ez a modul egy újabb Xposed verziót igényel (%d), ezért nem aktiválható</string>\n    <string name=\"warning_target_version_higher\">Ez a modul egy újabb Xposed verzióhoz készült (%d), ezért előfordulhat, hogy egyes funkciók nem működnek</string>\n    <string name=\"no_min_version_specified\">Ez a modul nem határoz meg szükséges Xposed verziót.</string>\n    <string name=\"warning_min_version_too_low\">Ez a modul az Xposed %1$dverziójához készült, de a %2$dverzióban bekövetkezett inkompatibilis változások miatt letiltásra került.</string>\n    <string name=\"warning_installed_on_external_storage\">Ez a modul nem tölthető be, mert az SD-kártyára van telepítve, kérjük, helyezze át a belső tárhelyre.</string>\n    <string name=\"module_uninstall\">Eltávolítás</string>\n    <string name=\"module_settings\">Modul beállítások</string>\n    <string name=\"view_in_repo\">Megtekintés a Repóban</string>\n    <string name=\"module_uninstall_message\">Szeretné eltávolítani ezt a modult?</string>\n    <string name=\"module_uninstalled\">%1$s eltávolítva</string>\n    <string name=\"module_uninstall_failed\">Az eltávolítás sikertelen</string>\n    <string name=\"add_module_to_user\">Modul hozzáadása a felhasználóhoz</string>\n    <string name=\"module_installed\">%1$s hozzáadva a(z) %2$s felhasználóhoz</string>\n    <string name=\"module_install_failed\">A modul hozzáadása sikertelen</string>\n    <string name=\"install_to_user\">Telepítés a felhasználóhoz %s</string>\n    <string name=\"install_to_user_message\">Szeretné telepíteni a %1$s -t a %2$sfelhasználóhoz ? Javasoljuk a manuális telepítést, az LSPosed-en keresztül történő kényszerített telepítés problémákat okozhat.</string>\n    <string name=\"expand\">kiterjesztés</string>\n    <string name=\"collapse\">összecsukás</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Újraoptimalizálás</string>\n    <string name=\"compile_speed_msg\">Optimalizálás…</string>\n    <string name=\"compile_done\">Az optimalizálás befejezve</string>\n    <string name=\"app_launch\">Indítsd el</string>\n    <string name=\"compile_failed\">Optimalizálás sikertelen: a visszatérési érték üres</string>\n    <string name=\"compile_failed_with_info\">Az optimalizálás nem sikerült: </string>\n    <string name=\"sort_by_name\">Alkalmazás neve</string>\n    <string name=\"sort_by_package_name\">Csomag neve</string>\n    <string name=\"sort_by_install_time\">Telepítés ideje</string>\n    <string name=\"sort_by_update_time\">Frissítés ideje</string>\n    <string name=\"sort_reverse\">Fordított</string>\n    <string name=\"menu_show_system_apps\">Rendszeralkalmazások</string>\n    <string name=\"menu_sort\">Rendezés</string>\n    <string name=\"enable_module\">Modul engedélyezése</string>\n    <string name=\"no_scope_selected\">Nem választott ki egyetlen alkalmazást sem. Folytatja?</string>\n    <string name=\"menu_show_games\">Játékok</string>\n    <string name=\"menu_show_modules\">Modulok</string>\n    <string name=\"menu_show_denylist\">Tiltólista</string>\n    <string name=\"failed_to_save_scope_list\">Nem sikerült elmenteni a hatókör listát</string>\n    <string name=\"app_version\">Verzió: %1$s</string>\n    <string name=\"use_recommended\">Ajánlott</string>\n    <string name=\"no_scope_selected_has_recommended\">Nem választott ki egyetlen alkalmazást sem. Kiválasztja az ajánlott alkalmazásokat?</string>\n    <string name=\"use_recommended_message\">Kiválasztja az ajánlott alkalmazásokat?</string>\n    <string name=\"module_is_not_activated_yet\">Az Xposed modul még nincs aktiválva</string>\n    <string name=\"requested_by_module\">Ajánlott</string>\n    <string name=\"update_available\">Frissítés elérhető: %1$s</string>\n    <string name=\"module_disabled_no_selection\">A %s modul le lett tiltva, mivel nincs kiválasztott alkalmazás.</string>\n    <string name=\"android_framework\">Rendszer keretrendszer</string>\n    <string name=\"menu_backup_and_restore\">Biztonsági mentés</string>\n    <string name=\"menu_backup\">Biztonsági mentés</string>\n    <string name=\"menu_restore\">Visszaállítás</string>\n    <string name=\"force_stop\">Erőszakos megállás</string>\n    <string name=\"force_stop_dlg_title\">Erőszakos megállás?</string>\n    <string name=\"force_stop_dlg_text\">Ha egy alkalmazást erőltetett leállítással állít le, az rosszul viselkedhet.</string>\n    <string name=\"reboot_required\">A módosítás érvényesítéséhez újraindítás szükséges</string>\n    <string name=\"reboot\">Újraindítás</string>\n    <string name=\"menu_hide\">Rejtsd el</string>\n    <string name=\"deny_list\">%s a denyliston van. Lehet, hogy nem lép hatályba.</string>\n    <string name=\"deny_list_info\">A denylistán</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Megtekintés más alkalmazásban</string>\n    <string name=\"module_app_info\">Alkalmazás információ</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nItt nincs semmi</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Keretrendszer</string>\n    <string name=\"settings_disable_verbose_log\">A szöveges naplózás kikapcsolása</string>\n    <string name=\"settings_disable_verbose_log_summary\">Jelentési kérdések kérése a verbózus naplók felvételére</string>\n    <string name=\"pure_black_dark_theme\">Fekete sötét téma</string>\n    <string name=\"pure_black_dark_theme_summary\">Teljesen fekete téma használata, ha a sötét téma engedélyezve van</string>\n    <string name=\"settings_group_theme\">Téma</string>\n    <string name=\"settings_backup_and_restore\">Biztonsági mentés és visszaállítás</string>\n    <string name=\"settings_backup_summery\">A modul lista és hatókör listák biztonsági mentése.</string>\n    <string name=\"settings_restore_summery\">A modullista és a hatókörlisták visszaállítása.</string>\n    <string name=\"settings_backup\">Biztonsági mentés</string>\n    <string name=\"settings_backup_failed2\">A biztonsági mentés sikertelen:\\n%s</string>\n    <string name=\"enable_documentui\">Kérjük, engedélyezze a DocumentUI-t</string>\n    <string name=\"settings_restore\">Visszaállítás</string>\n    <string name=\"settings_restore_failed2\">Nem sikerült visszaállítani:\\n%s</string>\n    <string name=\"group_network\">Hálózat</string>\n    <string name=\"dns_over_http\">DNS HTTPS-en keresztül</string>\n    <string name=\"dns_over_http_summary\">Megoldás DNS-mérgezés esetén egyes országokban</string>\n    <string name=\"theme_color\">Téma színe</string>\n    <string name=\"theme_color_system\">Rendszertéma színe</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Az alkalmazások kényszerítése az indító ikonok megjelenítésére</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Az Android 10 után az alkalmazások nem rejthetik el az indítóikonjaikat. Kapcsolja ki a kapcsolót ennek a rendszerfunkciónak a letiltásához.</string>\n    <string name=\"settings_group_system\">Rendszer</string>\n    <string name=\"settings_language\">Nyelv</string>\n    <string name=\"settings_translation_contributors\">A fordításban közreműködők</string>\n    <string name=\"settings_translation\">Részvétel a fordításban</string>\n    <string name=\"settings_translation_summary\">Segítsen nekünk lefordítani az %s -t az Ön nyelvére</string>\n    <string name=\"settings_create_shortcut_summary\">Hozzon létre egy parancsikont, amely képes megnyitni a parazita menedzser</string>\n    <string name=\"settings_shortcut_pinned_hint\">Parancsikon kitüzve</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">A jelenlegi alapértelmezett indítóprogram nem támogatja a pin parancsikonokat</string>\n    <string name=\"settings_enable_status_notification\">Állapot értesítés</string>\n    <string name=\"settings_enable_status_notification_summary\">Értesítés megjelenítése, amely megnyithatja a parazita-kezelőt</string>\n    <string name=\"disable_status_notification_error\">Nincs parancsikon, nem lehet letiltani az értesítést</string>\n    <string name=\"settings_update_channel\">Frissítési csatorna</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Béta</string>\n    <string name=\"update_channel_nightly\">Nightly</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API hívásvédelem</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">A dinamikusan betöltött modul kódjának blokkolása az Xposed API használatához, ez néhány modult tönkretehet, de a biztonság javára válik</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Olvass el</string>\n    <string name=\"module_releases\">Kiadások</string>\n    <string name=\"module_information\">Információ</string>\n    <string name=\"module_information_homepage\">Honlap</string>\n    <string name=\"module_information_source_url\">Forráskód</string>\n    <string name=\"module_information_collaborators\">Együttműködők</string>\n    <string name=\"module_release_view_assets\">Eszközök</string>\n    <string name=\"menu_open_in_browser\">Megnyitás a böngészőben</string>\n    <string name=\"module_release_load_more\">Régebbi verziók megjelenítése</string>\n    <string name=\"module_release_no_more\">Nincs több kiadás</string>\n    <string name=\"repo_load_failed\">Nem sikerült betölteni a modul repo-t: %s</string>\n    <string name=\"sort_upgradable_first\">Frissíthetőek először</string>\n    <string name=\"installed\">Telepítve</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d letöltés</item>\n        <item quantity=\"other\">%d letöltések</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Piros</string>\n    <string name=\"color_pink\">Rózsaszín</string>\n    <string name=\"color_purple\">Lila</string>\n    <string name=\"color_deep_purple\">Mély lila</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Kék</string>\n    <string name=\"color_light_blue\">Világoskék</string>\n    <string name=\"color_cyan\">Cián</string>\n    <string name=\"color_teal\">Zöldeskék</string>\n    <string name=\"color_green\">Zöld</string>\n    <string name=\"color_light_green\">Világoszöld</string>\n    <string name=\"color_lime\">Lime</string>\n    <string name=\"color_yellow\">Sárga</string>\n    <string name=\"color_amber\">Borostyán</string>\n    <string name=\"color_orange\">Narancs</string>\n    <string name=\"color_deep_orange\">Mély narancssárga</string>\n    <string name=\"color_brown\">Barna</string>\n    <string name=\"color_blue_grey\">Kékes szürke</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-in/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Ringkasan</string>\n    <string name=\"Modules\">Modul</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">%d modul diaktifkan</item>\n    </plurals>\n    <string name=\"Logs\">Log</string>\n    <string name=\"Settings\">Pengaturan</string>\n    <string name=\"feedback_or_suggestion\">Umpan balik atau saran</string>\n    <string name=\"About\">Tentang</string>\n    <string name=\"report_issue\">Laporkan masalah</string>\n    <string name=\"module_repo\">Gudang</string>\n    <string name=\"module_repo_up_to_date\">Semua modul sudah terbaru</string>\n    <string name=\"module_repo_published_time\">Diterbitkan pada %s</string>\n    <string name=\"module_repo_updated_time\">Diperbarui pada %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d modul dapat ditingkatkan</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Lihat kode sumber di %1$s<br/>Gabung dengan kami di saluran %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">pɹɐɥllıʇS</string>\n    <string name=\"install\">Pasang</string>\n    <string name=\"install_summary\">Ketuk untuk memasang LSPosed</string>\n    <string name=\"not_installed\">Tidak terpasang</string>\n    <string name=\"not_install_summary\">LSPosed tidak terpasang</string>\n    <string name=\"activated\">Diaktifkan</string>\n    <string name=\"partial_activated\">Diaktifkan sebagian</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy tidak dimuat dengan benar</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Beberapa fitur tidak akan berfungsi.<br/>Harap laporkan ini ke pengembang <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Injeksi Kerangka Sistem gagal</string>\n    <string name=\"system_inject_fail\"><![CDATA[Ini jarang terjadi dan mungkin disebabkan oleh <b>Magisk</b> atau beberapa modul Magisk berkualitas rendah.<br/>Coba nonaktifkan modul Magisk selain Riru dan LSPosed atau kirimkan log lengkap ke pengembang.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Prop sistem salah</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Beberapa properti sistem yang diperlukan dihapus atau diubah.<br/>Modul terkadang tidak valid.]]></string>\n    <string name=\"need_update\">Butuh pembaruan</string>\n    <string name=\"please_update_summary\">Silakan pasang LSPosed versi terbaru</string>\n    <string name=\"developer_warning_title\">Tips untuk pengembang modul</string>\n    <string name=\"developer_warning_summary\">Harap nonaktifkan pengoptimalan penerapan di Android Studio, atau gunakan perintah `gradlew installDebug` untuk menginstal. Jika tidak, apk modul tidak akan diperbarui.</string>\n    <string name=\"info_api_version\">Versi API</string>\n    <string name=\"info_framework_version\">Versi framework</string>\n    <string name=\"info_manager_package_name\">Nama paket manajer</string>\n    <string name=\"info_system_version\">Versi sistem</string>\n    <string name=\"info_device\">Perangkat</string>\n    <string name=\"info_system_abi\">Sistem ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Pengoptimal Pengemas Dex</string>\n    <string name=\"enabled\">Diaktifkan</string>\n    <string name=\"not_enabled\">Tidak diaktifkan</string>\n    <string name=\"supported\">Didukung</string>\n    <string name=\"unsupported\">Tidak didukung</string>\n    <string name=\"android_version_unsatisfied\">Versi Android tidak tersedia</string>\n    <string name=\"crashed\">Rusak</string>\n    <string name=\"mount_failed\">Mount gagal</string>\n    <string name=\"selinux_permissive\">SELinux permisif</string>\n    <string name=\"sepolicy_incorrect\">SELinux policy salah</string>\n    <string name=\"update_lsposed\">Perbarui LSPosed</string>\n    <string name=\"update_lsposed_msg\">Konfirmasi untuk pembaruan LSPosed? Perangkat ini akan mulai ulang setelah pembaruan selesai</string>\n    <string name=\"info_copied\">Disalin ke papan klip</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Selamat datang di LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Anda menggunakan manajer parasit, yang dapat membuat pintasan atau dapat terbuka dari notifikasi.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Anda menggunakan manajer parasit, yang dapat dibuka dari notifikasi.</string>\n    <string name=\"create_shortcut\">Buat pintasan</string>\n    <string name=\"never_show\">Jangan pernah tampilkan</string>\n    <string name=\"app_welcome\">Manajer Parasit Direkomendasikan</string>\n    <string name=\"app_welcome_summary\">LSPosed sekarang mendukung parasitisasi sistem untuk menghindari deteksi, Anda dapat membuka manajer parasit dari pemberitahuan. Disarankan untuk menghapus aplikasi saat ini.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Simpan</string>\n    <string name=\"nav_item_logs_verbose\">Log Verbose</string>\n    <string name=\"nav_item_logs_module\">Log Modul</string>\n    <string name=\"logs_saving\">Menyimpan log, harap tunggu</string>\n    <string name=\"logs_saved\">Log disimpan</string>\n    <string name=\"logs_save_failed2\">Gagal menyimpan:\\n%s</string>\n    <string name=\"menuClearLog\">Hapus log sekarang</string>\n    <string name=\"logs_cleared\">Log berhasil dihapus.</string>\n    <string name=\"scroll_top\">Gulir ke atas</string>\n    <string name=\"loading\">Memuat…</string>\n    <string name=\"scroll_bottom\">Gulir ke bawah</string>\n    <string name=\"menuReload\">Muat ulang</string>\n    <string name=\"logs_clear_failed_2\">Gagal menghapus log</string>\n    <string name=\"menu_enable_word_wrap\">Bungkus Kata</string>\n    <string name=\"enabled_verbose_log\">Log verbose diaktifkan</string>\n    <string name=\"disabled_verbose_log\">Log verbose dinonaktifkan</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(tidak ada deskripsi yang diberikan)</string>\n    <string name=\"warning_xposed_min_version\">Modul ini memerlukan versi Xposed yang lebih baru (%d) sehingga tidak dapat diaktifkan</string>\n    <string name=\"warning_target_version_higher\">Modul ini dirancang untuk versi Xposed yang lebih baru (%d) sehingga beberapa fungsi mungkin tidak berfungsi</string>\n    <string name=\"no_min_version_specified\">Modul ini tidak menentukan versi Xposed yang diperlukan.</string>\n    <string name=\"warning_min_version_too_low\">Modul ini dibuat untuk Xposed versi %1$d, tetapi karena perubahan yang tidak kompatibel di versi %2$d, modul ini telah dinonaktifkanModul ini dibuat untuk Xposed versi %1$d, tetapi karena perubahan yang tidak kompatibel di versi %2$d, modul ini telah dinonaktifkan</string>\n    <string name=\"warning_installed_on_external_storage\">Modul ini tidak dapat dimuat karena terpasang di kartu SD, harap pindahkan ke penyimpanan internal</string>\n    <string name=\"module_uninstall\">Copot</string>\n    <string name=\"module_settings\">Pengaturan modul</string>\n    <string name=\"view_in_repo\">Lihat di Repo</string>\n    <string name=\"module_uninstall_message\">Apakah Anda ingin mencopot modul ini?</string>\n    <string name=\"module_uninstalled\">Tercopot %1$s</string>\n    <string name=\"module_uninstall_failed\">Copot pemasangan tidak berhasil</string>\n    <string name=\"add_module_to_user\">Tambahkan modul ke pengguna</string>\n    <string name=\"module_installed\">Ditambahkan %1$s ke pengguna %2$s</string>\n    <string name=\"module_install_failed\">Gagal menambahkan modul</string>\n    <string name=\"install_to_user\">Pasang ke pengguna %s</string>\n    <string name=\"install_to_user_message\">Ingin memasang %1$s ke pengguna %2$s? Disarankan untuk memasang secara manual, memaksa pemasangan melalui LSPosed dapat menyebabkan masalah.</string>\n    <string name=\"expand\">perluas</string>\n    <string name=\"collapse\">ciutkan</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Mengoptimalkan ulang</string>\n    <string name=\"compile_speed_msg\">Mengoptimalkan…</string>\n    <string name=\"compile_done\">Optimalisasi selesai</string>\n    <string name=\"app_launch\">Luncurkan</string>\n    <string name=\"compile_failed\">Optimalisasi gagal: nilai yang dihasilkan kosong</string>\n    <string name=\"compile_failed_with_info\">Optimalisasi gagal: </string>\n    <string name=\"sort_by_name\">Nama aplikasi</string>\n    <string name=\"sort_by_package_name\">Nama paket</string>\n    <string name=\"sort_by_install_time\">Waktu pemasangan</string>\n    <string name=\"sort_by_update_time\">Waktu pembaruan</string>\n    <string name=\"sort_reverse\">Terbalik</string>\n    <string name=\"menu_show_system_apps\">Aplikasi sistem</string>\n    <string name=\"menu_sort\">Penyortiran</string>\n    <string name=\"enable_module\">Aktifkan modul</string>\n    <string name=\"no_scope_selected\">Anda tidak memilih aplikasi apa pun. Lanjutkan?</string>\n    <string name=\"menu_show_games\">Permainan</string>\n    <string name=\"menu_show_modules\">Modul</string>\n    <string name=\"menu_show_denylist\">Daftar penolakan</string>\n    <string name=\"failed_to_save_scope_list\">Gagal menyimpan ke daftar cakupan</string>\n    <string name=\"app_version\">Versi: %1$s</string>\n    <string name=\"use_recommended\">Direkomendasikan</string>\n    <string name=\"no_scope_selected_has_recommended\">Anda tidak memilih aplikasi apapun. Pilih aplikasi yang disarankan?</string>\n    <string name=\"use_recommended_message\">Pilih aplikasi yang disarankan?</string>\n    <string name=\"module_is_not_activated_yet\">Modul Xposed belum diaktifkan</string>\n    <string name=\"requested_by_module\">Direkomendasikan</string>\n    <string name=\"update_available\">Pembaruan tersedia: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s telah dinonaktifkan karena tidak ada aplikasi yang dipilih.</string>\n    <string name=\"android_framework\">Kerangka kerja sistem</string>\n    <string name=\"menu_backup_and_restore\">Cadangan</string>\n    <string name=\"menu_backup\">Cadangkan</string>\n    <string name=\"menu_restore\">Pulihkan</string>\n    <string name=\"force_stop\">Paksa berhenti</string>\n    <string name=\"force_stop_dlg_title\">Paksa berhenti?</string>\n    <string name=\"force_stop_dlg_text\">Jika Anda menghentikan paksa aplikasi, mungkin dapat bekerja tidak semestinya.</string>\n    <string name=\"reboot_required\">Mulai ulang diperlukan agar perubahan ini dapat diterapkan</string>\n    <string name=\"reboot\">Mulai ulang</string>\n    <string name=\"menu_hide\">Sembunyikan</string>\n    <string name=\"deny_list\">%s ada dalam daftar penolakan. Ini mungkin tidak berpengaruh.</string>\n    <string name=\"deny_list_info\">Berada di daftar tolak</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Lihat di aplikasi lain</string>\n    <string name=\"module_app_info\">Informasi aplikasi</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nTidak ada apa-apa di sini</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Kerangka kerja</string>\n    <string name=\"settings_disable_verbose_log\">Nonaktifkan log verbose</string>\n    <string name=\"settings_disable_verbose_log_summary\">Permintaan laporan masalah dengan menyertakan log-log verbose</string>\n    <string name=\"settings_enable_log_watchdog\">Aktifkan watchdog</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Catatan watchdog LSPosed memodifikasi properti sistem, yang dapat diekploitasi untuk mendeteksi LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Tema hitam gelap</string>\n    <string name=\"pure_black_dark_theme_summary\">Gunakan tema hitam murni jika tema gelap diaktifkan</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Cadangkan dan pulihkan</string>\n    <string name=\"settings_backup_summery\">Cadangkan daftar modul dan daftar cakupan.</string>\n    <string name=\"settings_restore_summery\">Pulihkan daftar modul dan daftar cakupan.</string>\n    <string name=\"settings_backup\">Cadangkan</string>\n    <string name=\"settings_backup_failed2\">Gagal mencadangkan:\\n%s</string>\n    <string name=\"enable_documentui\">Harap aktifkan DocumentUI</string>\n    <string name=\"settings_restore\">Pulihkan</string>\n    <string name=\"settings_restore_failed2\">Gagal memulihkan:\\n%s</string>\n    <string name=\"group_network\">Jaringan</string>\n    <string name=\"dns_over_http\">DNS melalui HTTPS</string>\n    <string name=\"dns_over_http_summary\">Solusi mengatasi masalah DNS di beberapa negara</string>\n    <string name=\"theme_color\">Warna tema</string>\n    <string name=\"theme_color_system\">Warna tema sistem</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Paksa aplikasi untuk menampilkan ikon peluncur</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Setelah Android 10, aplikasi tidak diizinkan menyembunyikan ikon peluncurnya. Matikan untuk menonaktifkan fitur sistem ini.</string>\n    <string name=\"settings_group_system\">Sistem</string>\n    <string name=\"settings_language\">Bahasa</string>\n    <string name=\"settings_translation_contributors\">Kontributor terjemahan</string>\n    <string name=\"settings_translation\">Berpartisipasi dalam terjemahan</string>\n    <string name=\"settings_translation_summary\">Bantu kami menerjemahkan %s ke dalam bahasamu</string>\n    <string name=\"settings_create_shortcut_summary\">Buat pintasan yang dapat membuka manajer parasit</string>\n    <string name=\"settings_shortcut_pinned_hint\">Pintasan disematkan</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Peluncur default saat ini tidak mendukung pintasan pin</string>\n    <string name=\"settings_enable_status_notification\">Notifikasi Status</string>\n    <string name=\"settings_enable_status_notification_summary\">Tampilkan notifikasi yang dapat membuka manajer parasit</string>\n    <string name=\"disable_status_notification_error\">Tidak ada pintasan, tidak dapat mematikan notifikasi</string>\n    <string name=\"settings_update_channel\">Perbarui saluran</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Rilis harian</string>\n    <string name=\"settings_xposed_api_call_protection\">Proteksi panggilan API Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokir kode modul yang dimuat secara dinamis untuk menggunakan Xposed API, ini mungkin mengganggu beberapa modul namun lebih aman</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Baca aku</string>\n    <string name=\"module_releases\">Rilis</string>\n    <string name=\"module_information\">Informasi</string>\n    <string name=\"module_information_homepage\">Beranda</string>\n    <string name=\"module_information_source_url\">Kode sumber</string>\n    <string name=\"module_information_collaborators\">Kolaborator</string>\n    <string name=\"module_release_view_assets\">Aset</string>\n    <string name=\"menu_open_in_browser\">Buka di browser</string>\n    <string name=\"module_release_load_more\">Tampilkan versi lama</string>\n    <string name=\"module_release_no_more\">Tidak ada rilis</string>\n    <string name=\"repo_load_failed\">Gagal memuat repo modul: %s</string>\n    <string name=\"sort_upgradable_first\">Dapat diupgrade terlebih dahulu</string>\n    <string name=\"installed\">Terpasang</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d unduh\n%d unduhan</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Merah</string>\n    <string name=\"color_pink\">Merah muda</string>\n    <string name=\"color_purple\">Ungu</string>\n    <string name=\"color_deep_purple\">Ungu gelap</string>\n    <string name=\"color_indigo\">Biru gelap</string>\n    <string name=\"color_blue\">Biru</string>\n    <string name=\"color_light_blue\">Biru muda</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Hijau toska</string>\n    <string name=\"color_green\">Hijau</string>\n    <string name=\"color_light_green\">Hijau muda</string>\n    <string name=\"color_lime\">Hijau limau</string>\n    <string name=\"color_yellow\">Kuning</string>\n    <string name=\"color_amber\">Kuning madu</string>\n    <string name=\"color_orange\">Jingga</string>\n    <string name=\"color_deep_orange\">Jingga gelap</string>\n    <string name=\"color_brown\">Coklat</string>\n    <string name=\"color_blue_grey\">Abu-abu kebiruan</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Panoramica</string>\n    <string name=\"Modules\">Moduli</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modulo abilitato</item>\n        <item quantity=\"other\">%d moduli abilitati</item>\n    </plurals>\n    <string name=\"Logs\">Log</string>\n    <string name=\"Settings\">Impostazioni</string>\n    <string name=\"feedback_or_suggestion\">Feedback o suggerimenti</string>\n    <string name=\"About\">Informazioni</string>\n    <string name=\"report_issue\">Segnala il problema</string>\n    <string name=\"module_repo\">Repository</string>\n    <string name=\"module_repo_up_to_date\">Tutti i moduli sono aggiornati</string>\n    <string name=\"module_repo_published_time\">Pubblicato alle %s</string>\n    <string name=\"module_repo_updated_time\">Aggiornato alle %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modulo aggiornabile</item>\n        <item quantity=\"other\">%d moduli aggiornabili</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Visualizza il codice sorgente su %1$s<br/>Unisciti al nostro canale %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">alex193a, Fs00, xDonatello</string>\n    <string name=\"install\">Installa</string>\n    <string name=\"install_summary\">Tocca per installare LSPosed</string>\n    <string name=\"not_installed\">Non installato</string>\n    <string name=\"not_install_summary\">LSPosed non è installato</string>\n    <string name=\"activated\">Attivo</string>\n    <string name=\"partial_activated\">Parzialmente attivo</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy non è caricato correttamente</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Alcune funzionalità non funzioneranno.<br/>Segnalalo allo sviluppatore di <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Injection del framework di sistema fallita</string>\n    <string name=\"system_inject_fail\"><![CDATA[L\\'injection del framework di sistema è fallita.<br/>Questo problema si verifica raramente e può essere causato da <b>Magisk</b> o da alcuni moduli Magisk di scarsa qualità.<br/>Prova a disabilitare i moduli Magisk tranne Riru e LSPosed o invia il log completo agli sviluppatori.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Proprietà di sistema errate</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Alcune proprietà di sistema necessarie sono state eliminate o modificate.<br/>In alcuni casi i moduli potrebbero non funzionare.]]></string>\n    <string name=\"need_update\">Aggiornamento richiesto</string>\n    <string name=\"please_update_summary\">Installa la versione più recente di LSPosed</string>\n    <string name=\"developer_warning_title\">Suggerimenti per lo sviluppatore del modulo</string>\n    <string name=\"developer_warning_summary\">Disattivare le ottimizzazioni di distribuzione su Android Studio, o utilizzare il comando `gradlew installDebug` per eseguire l\\'installazione. Altrimenti l\\'apk del modulo non verrà aggiornato.</string>\n    <string name=\"info_api_version\">Versione API</string>\n    <string name=\"info_framework_version\">Versione del framework</string>\n    <string name=\"info_manager_package_name\">Nome pacchetto del manager</string>\n    <string name=\"info_system_version\">Versione del sistema</string>\n    <string name=\"info_device\">Dispositivo</string>\n    <string name=\"info_system_abi\">ABI del sistema</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Abilitato</string>\n    <string name=\"not_enabled\">Non abilitato</string>\n    <string name=\"supported\">Supportato</string>\n    <string name=\"unsupported\">Non supportato</string>\n    <string name=\"android_version_unsatisfied\">Versione di Android non soddisfatta</string>\n    <string name=\"crashed\">Arrestato in modo anomalo</string>\n    <string name=\"mount_failed\">Mount fallito</string>\n    <string name=\"selinux_permissive\">SELinux è in modalità permissiva</string>\n    <string name=\"sepolicy_incorrect\">La politica di SELinux non è corretta</string>\n    <string name=\"update_lsposed\">Aggiorna LSPosed</string>\n    <string name=\"update_lsposed_msg\">Confermi di voler aggiornare LSPosed? Il dispositivo verrà riavviato dopo il completamento dell\\'aggiornamento</string>\n    <string name=\"info_copied\">Copiato negli appunti</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Benvenuto in LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Stai usando il manager parassitario, che può creare scorciatoie o essere aperto dalla notifica.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Stai usando il manager parassitario, che può essere aperto dalla notifica.</string>\n    <string name=\"create_shortcut\">Crea scorciatoia</string>\n    <string name=\"never_show\">Non mostrare mai</string>\n    <string name=\"app_welcome\">Manager parassitario consigliato</string>\n    <string name=\"app_welcome_summary\">LSPosed ora supporta la parassitazione del sistema per evitarne il rilevamento, è possibile aprire il manager parassitario dalla notifica. Si consiglia di disinstallare l\\'applicazione attuale.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Salva</string>\n    <string name=\"nav_item_logs_verbose\">Log verbosi</string>\n    <string name=\"nav_item_logs_module\">Log dei moduli</string>\n    <string name=\"logs_saving\">Salvataggio log, attendere</string>\n    <string name=\"logs_saved\">Log salvati</string>\n    <string name=\"logs_save_failed2\">Salvataggio non riuscito:\\n%s</string>\n    <string name=\"menuClearLog\">Cancella il log ora</string>\n    <string name=\"logs_cleared\">Log cancellato con successo.</string>\n    <string name=\"scroll_top\">Scorri in alto</string>\n    <string name=\"loading\">Caricamento in corso…</string>\n    <string name=\"scroll_bottom\">Scorri in basso</string>\n    <string name=\"menuReload\">Ricarica</string>\n    <string name=\"logs_clear_failed_2\">Impossibile cancellare il log</string>\n    <string name=\"menu_enable_word_wrap\">A capo automatico</string>\n    <string name=\"enabled_verbose_log\">Log verboso abilitato</string>\n    <string name=\"disabled_verbose_log\">Log verboso disabilitato</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(nessuna descrizione fornita)</string>\n    <string name=\"warning_xposed_min_version\">Questo modulo richiede una versione più recente di Xposed (%d) e quindi non può essere attivato</string>\n    <string name=\"warning_target_version_higher\">Questo modulo è progettato per una versione più recente di Xposed (%d) e quindi alcune funzionalità potrebbero non funzionare</string>\n    <string name=\"no_min_version_specified\">Questo modulo non specifica la versione Xposed necessaria.</string>\n    <string name=\"warning_min_version_too_low\">Questo modulo è stato creato per la versione %1$d di Xposed ma, a causa di modifiche incompatibili nella versione %2$d, è stato disabilitato</string>\n    <string name=\"warning_installed_on_external_storage\">Questo modulo non può essere caricato perché è installato sulla scheda SD, spostalo nella memoria interna</string>\n    <string name=\"module_uninstall\">Disinstalla</string>\n    <string name=\"module_settings\">Impostazioni modulo</string>\n    <string name=\"view_in_repo\">Visualizza nel repository</string>\n    <string name=\"module_uninstall_message\">Vuoi disinstallare questo modulo?</string>\n    <string name=\"module_uninstalled\">%1$s disinstallato</string>\n    <string name=\"module_uninstall_failed\">Disinstallazione non riuscita</string>\n    <string name=\"add_module_to_user\">Aggiungi modulo all\\'utente</string>\n    <string name=\"module_installed\">%1$s aggiunto all\\'utente %2$s</string>\n    <string name=\"module_install_failed\">Aggiunta del modulo fallita</string>\n    <string name=\"install_to_user\">Installa per l\\'utente %s</string>\n    <string name=\"install_to_user_message\">Vuoi installare %1$s per l\\'utente %2$s? Si consiglia di farlo manualmente, forzare l\\'installazione da LSPosed potrebbe causare problemi.</string>\n    <string name=\"expand\">espandi</string>\n    <string name=\"collapse\">comprimi</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Ri-ottimizza</string>\n    <string name=\"compile_speed_msg\">Ottimizzazione in corso…</string>\n    <string name=\"compile_done\">Ottimizzazione completata</string>\n    <string name=\"app_launch\">Avvia</string>\n    <string name=\"compile_failed\">Ottimizzazione non riuscita: il valore restituito è vuoto</string>\n    <string name=\"compile_failed_with_info\">Ottimizzazione fallita: </string>\n    <string name=\"sort_by_name\">Nome dell\\'applicazione</string>\n    <string name=\"sort_by_package_name\">Nome del pacchetto</string>\n    <string name=\"sort_by_install_time\">Data di installazione</string>\n    <string name=\"sort_by_update_time\">Data di aggiornamento</string>\n    <string name=\"sort_reverse\">Inverso</string>\n    <string name=\"menu_show_system_apps\">Applicazioni di sistema</string>\n    <string name=\"menu_sort\">Ordina</string>\n    <string name=\"enable_module\">Abilita modulo</string>\n    <string name=\"no_scope_selected\">Non hai selezionato nessuna app. Continuare?</string>\n    <string name=\"menu_show_games\">Giochi</string>\n    <string name=\"menu_show_modules\">Moduli</string>\n    <string name=\"menu_show_denylist\">Lista di blocco</string>\n    <string name=\"failed_to_save_scope_list\">Impossibile salvare l\\'elenco delle attivazioni</string>\n    <string name=\"app_version\">Versione: %1$s</string>\n    <string name=\"use_recommended\">Seleziona consigliate</string>\n    <string name=\"no_scope_selected_has_recommended\">Non hai selezionato nessuna app. Selezionare le app consigliate?</string>\n    <string name=\"use_recommended_message\">Selezionare le app consigliate?</string>\n    <string name=\"module_is_not_activated_yet\">Il modulo Xposed non è ancora attivo</string>\n    <string name=\"requested_by_module\">Seleziona consigliate</string>\n    <string name=\"update_available\">Aggiornamento disponibile: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Il modulo %s è stato disabilitato poiché nessuna app è stata selezionata.</string>\n    <string name=\"android_framework\">Framework di sistema</string>\n    <string name=\"menu_backup_and_restore\">Backup</string>\n    <string name=\"menu_backup\">Backup</string>\n    <string name=\"menu_restore\">Ripristina</string>\n    <string name=\"force_stop\">Forza l\\'arresto</string>\n    <string name=\"force_stop_dlg_title\">Forzare l\\'arresto?</string>\n    <string name=\"force_stop_dlg_text\">Se forzi l\\'interruzione di un\\'app, potrebbe non funzionare correttamente.</string>\n    <string name=\"reboot_required\">È necessario riavviare per applicare questa modifica</string>\n    <string name=\"reboot\">Riavvia</string>\n    <string name=\"menu_hide\">Nascondi</string>\n    <string name=\"deny_list\">%s è nella lista di blocco. Potrebbe non avere effetto.</string>\n    <string name=\"deny_list_info\">Nella lista di blocco</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Mostra in un\\'altra app</string>\n    <string name=\"module_app_info\">Informazioni app</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\\\/¯\\nNon c\\'è nulla qui</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Disabilita il log verboso</string>\n    <string name=\"settings_disable_verbose_log_summary\">La segnalazione di problemi richiede l\\'inclusione di log dettagliati</string>\n    <string name=\"settings_enable_log_watchdog\">Abilita log watchdog</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Il watchdog log di LSPosed modifica le proprietà del sistema, che potrebbero essere sfruttate per rilevare LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Tema nero scuro</string>\n    <string name=\"pure_black_dark_theme_summary\">Usa il tema nero puro quando è abilitato il tema scuro</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Backup e ripristino</string>\n    <string name=\"settings_backup_summery\">Backup dell\\'elenco dei moduli e delle attivazioni.</string>\n    <string name=\"settings_restore_summery\">Ripristino dell\\'elenco dei moduli e delle attivazioni.</string>\n    <string name=\"settings_backup\">Backup</string>\n    <string name=\"settings_backup_failed2\">Salvataggio non riuscito:\\n%s</string>\n    <string name=\"enable_documentui\">Abilitare DocumentUI</string>\n    <string name=\"settings_restore\">Ripristina</string>\n    <string name=\"settings_restore_failed2\">Ripristino fallito:\\n%s</string>\n    <string name=\"group_network\">Rete</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">Aggira l\\'avvelenamento della cache DNS in alcune nazioni</string>\n    <string name=\"theme_color\">Colore del tema</string>\n    <string name=\"theme_color_system\">Colore del tema del sistema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Forza le app a mostrare le icone nel launcher</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">A partire da Android 10, le app non possono più nascondere le loro icone nel launcher. Disabilita l\\'opzione per disattivare questa funzionalità.</string>\n    <string name=\"settings_group_system\">Sistema</string>\n    <string name=\"settings_language\">Lingua</string>\n    <string name=\"settings_translation_contributors\">Contributori alla traduzione</string>\n    <string name=\"settings_translation\">Partecipa alla traduzione</string>\n    <string name=\"settings_translation_summary\">Aiutaci a tradurre %s nella tua lingua</string>\n    <string name=\"settings_create_shortcut_summary\">Crea una scorciatoia che può aprire il manager parassitario</string>\n    <string name=\"settings_shortcut_pinned_hint\">Scorciatoia fissata</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">L\\'attuale launcher predefinito non supporta le scorciatoie con i pin</string>\n    <string name=\"settings_enable_status_notification\">Notifica di stato</string>\n    <string name=\"settings_enable_status_notification_summary\">Mostra una notifica che può aprire il manager parassitario</string>\n    <string name=\"disable_status_notification_error\">Nessuna scorciatoia, impossibile disattivare la notifica</string>\n    <string name=\"settings_update_channel\">Canale di aggiornamento</string>\n    <string name=\"update_channel_stable\">Stabile</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly</string>\n    <string name=\"settings_xposed_api_call_protection\">API di protezione delle chiamate di Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blocca dinamicamente il codice del modulo caricato per utilizzare l\\'API di Xposed, questo potrebbe interrompere alcuni moduli ma favorire la sicurezza</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Leggimi</string>\n    <string name=\"module_releases\">Versioni</string>\n    <string name=\"module_information\">Informazioni</string>\n    <string name=\"module_information_homepage\">Pagina web</string>\n    <string name=\"module_information_source_url\">Codice sorgente</string>\n    <string name=\"module_information_collaborators\">Collaboratori</string>\n    <string name=\"module_release_view_assets\">Risorse</string>\n    <string name=\"menu_open_in_browser\">Apri nel browser</string>\n    <string name=\"module_release_load_more\">Mostra le versioni precedenti</string>\n    <string name=\"module_release_no_more\">Non ci sono altre versioni</string>\n    <string name=\"repo_load_failed\">Impossibile caricare il repository dei moduli: %s</string>\n    <string name=\"sort_upgradable_first\">Aggiornabili prima</string>\n    <string name=\"installed\">Installato</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d download</item>\n        <item quantity=\"other\">%d downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rosso</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Viola</string>\n    <string name=\"color_deep_purple\">Viola scuro</string>\n    <string name=\"color_indigo\">Indaco</string>\n    <string name=\"color_blue\">Blu</string>\n    <string name=\"color_light_blue\">Azzurro</string>\n    <string name=\"color_cyan\">Ciano</string>\n    <string name=\"color_teal\">Verde acqua</string>\n    <string name=\"color_green\">Verde</string>\n    <string name=\"color_light_green\">Verde chiaro</string>\n    <string name=\"color_lime\">Lime</string>\n    <string name=\"color_yellow\">Giallo</string>\n    <string name=\"color_amber\">Ambra</string>\n    <string name=\"color_orange\">Arancione</string>\n    <string name=\"color_deep_orange\">Arancione scuro</string>\n    <string name=\"color_brown\">Marrone</string>\n    <string name=\"color_blue_grey\">Blu grigio</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-iw/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">סקירה כללית</string>\n    <string name=\"Modules\">מודולים</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d מודול מופעל</item>\n        <item quantity=\"two\">%d מודולים מופעלים</item>\n        <item quantity=\"many\">%d מודולים מופעלים</item>\n        <item quantity=\"other\">%d מודולים מופעלים</item>\n    </plurals>\n    <string name=\"Logs\">לוגים</string>\n    <string name=\"Settings\">הגדרות</string>\n    <string name=\"feedback_or_suggestion\">משוב או הצעה</string>\n    <string name=\"About\">אודות</string>\n    <string name=\"report_issue\">דווח על בעיה</string>\n    <string name=\"module_repo\">מאגר מידע</string>\n    <string name=\"module_repo_up_to_date\">כל המודולים מעודכנים</string>\n    <string name=\"module_repo_published_time\">פורסם ב-%s</string>\n    <string name=\"module_repo_updated_time\">עודכן ב-%s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d מודולים ניתנים לשדרוג</item>\n        <item quantity=\"two\">%d מודולים ניתנים לשדרוג</item>\n        <item quantity=\"many\">%d מודולים ניתנים לשדרוג</item>\n        <item quantity=\"other\">%d מודולים ניתנים לשדרוג</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[הצג קוד מקור ב-%1$s<br/> הצטרף לערוץ %2$s שלנו]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">ריק</string>\n    <string name=\"install\">התקנה</string>\n    <string name=\"install_summary\">הקש כדי להתקין את LSPosed</string>\n    <string name=\"not_installed\">לא מותקן</string>\n    <string name=\"not_install_summary\">LSPosed אינו מותקן</string>\n    <string name=\"activated\">הופעל</string>\n    <string name=\"partial_activated\">הופעל חלקית</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy לא נטען כהלכה</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[חלק מהתכונות לא יפעלו.<br/>נא לדווח על כך למפתחי Magisk</b></a> <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>.]]></string>\n    <string name=\"system_inject_fail_summary\">הזרקת תשתית המערכת נכשלה</string>\n    <string name=\"system_inject_fail\"><![CDATA[זה נדיר ועלול להיגרם על ידי <b>Magisk</b> או מודולי Magisk באיכות נמוכה.<br/>אנא נסה להשבית מודולי Magisk שאינם Riru ו-LSPosed או שלח דיווח לוג מלא למפתחים.]]></string>\n    <string name=\"system_prop_incorrect_summary\">מאפיין המערכת שגוי</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[חלק ממאפייני המערכת הדרושים נמחקו או שונו.<br/>המודולים עשויים להתבטל מדי פעם.]]></string>\n    <string name=\"need_update\">צריך לעדכן</string>\n    <string name=\"please_update_summary\">נא להתקין את הגרסה העדכנית ביותר של LSPosed</string>\n    <string name=\"info_api_version\">גרסת API</string>\n    <string name=\"info_framework_version\">גרסת תשתית</string>\n    <string name=\"info_manager_package_name\">שם חבילת מנהל</string>\n    <string name=\"info_system_version\">גרסת מערכת</string>\n    <string name=\"info_device\">מכשיר</string>\n    <string name=\"info_system_abi\">מערכת ABI</string>\n    <string name=\"info_dex2oat_wrapper\">עטיפה של Dex Optimizer</string>\n    <string name=\"enabled\">מופעל</string>\n    <string name=\"not_enabled\">לא מופעל</string>\n    <string name=\"supported\">נתמך</string>\n    <string name=\"unsupported\">לא נתמך</string>\n    <string name=\"android_version_unsatisfied\">גרסת אנדרואיד לא מספקת</string>\n    <string name=\"crashed\">התרסק</string>\n    <string name=\"mount_failed\">ה-mount נכשל</string>\n    <string name=\"selinux_permissive\">מדיניות SELinux הינה מתירנית</string>\n    <string name=\"sepolicy_incorrect\">מדיניות SELinux הינה שגויה</string>\n    <string name=\"update_lsposed\">עדכן LSPosed</string>\n    <string name=\"update_lsposed_msg\">אשר לעדכן את LSPosed? מכשיר זה יאתחל לאחר השלמת העדכון</string>\n    <string name=\"info_copied\">הועתק ללוח</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">ברוכים הבאים ל-LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">הנך משתמש בגרסת אפליקציית ניהול בתצורת טפיל, שיכולה ליצור קיצור דרך או עדיין להיפתח מהתראה.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">הנך משתמש בגרסת אפליקציית ניהול בתצורת טפיל, שיכולה להיפתח מהתראה.</string>\n    <string name=\"create_shortcut\">צור קיצור דרך</string>\n    <string name=\"never_show\">לעולם אל תראה</string>\n    <string name=\"app_welcome\">מומלצת גרסת אפליקציית ניהול בתצורת טפיל</string>\n    <string name=\"app_welcome_summary\">LSPosed תומך כעת בתצורת טפיל כדי למנוע זיהוי, ניתן לפתוח את גרסת אפליקציית הניהול בתצורת טפיל מהתראה. מומלץ להסיר את האפליקציה הנוכחית.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">שמור</string>\n    <string name=\"nav_item_logs_verbose\">לוגים מפורטים</string>\n    <string name=\"nav_item_logs_module\">לוגים של מודולים</string>\n    <string name=\"logs_saving\">שומר יומן, אנא המתן</string>\n    <string name=\"logs_saved\">לוגים נשמרו</string>\n    <string name=\"logs_save_failed2\">השמירה נכשלה:\\n%s</string>\n    <string name=\"menuClearLog\">נקה לוגים עכשיו</string>\n    <string name=\"logs_cleared\">לוגים נוקו בהצלחה.</string>\n    <string name=\"scroll_top\">גלול למעלה</string>\n    <string name=\"loading\">טוען…</string>\n    <string name=\"scroll_bottom\">גלול למטה</string>\n    <string name=\"menuReload\">רענן</string>\n    <string name=\"logs_clear_failed_2\">נכשל בניקוי הלוגים</string>\n    <string name=\"menu_enable_word_wrap\">עטיפת מילה</string>\n    <string name=\"enabled_verbose_log\">לוגים מפורטים מופעלים</string>\n    <string name=\"disabled_verbose_log\">לוגים מפורטים מושבתים</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(לא מסופק תיאור)</string>\n    <string name=\"warning_xposed_min_version\">מודול זה דורש גרסה חדשה יותר של LSPosed (%d) ולכן לא יכול להיות מופעל</string>\n    <string name=\"warning_target_version_higher\">מודול זה מיועד לגרסה חדשה יותר של Xposed (%d) ולכן ייתכן שחלק מהפונקציונליות לא תופעל כהלכה</string>\n    <string name=\"no_min_version_specified\">מודול זה לא מציין את גסרת ה- LSPosed שהוא צריך.</string>\n    <string name=\"warning_min_version_too_low\">מודול זה נוצר בשביל LSPosed גרסה %1$d, אך בשל חוסר תאימות לאחור בגרסה %2$d, הוא הופסק</string>\n    <string name=\"warning_installed_on_external_storage\">מודול זה לא יכול להיטען מכיוון שהוא מותקן על כרטיס ה-SD, אנא העבר אותו לאחסון פנימי</string>\n    <string name=\"module_uninstall\">הסר התקנה</string>\n    <string name=\"module_settings\">הגדרות מודול</string>\n    <string name=\"view_in_repo\">הצג ברפוסיטורי</string>\n    <string name=\"module_uninstall_message\">האם אתה בטוח שאתה רוצב להסיר את התנקת המודול?</string>\n    <string name=\"module_uninstalled\">%1$s הוסר</string>\n    <string name=\"module_uninstall_failed\">הסרת ההתקנה הושלמה בהצלחה</string>\n    <string name=\"add_module_to_user\">הוסף מודל למשתמש</string>\n    <string name=\"module_installed\">נוסף %1$s למשתמש %2$s</string>\n    <string name=\"module_install_failed\">הוספת המודל נכשלה</string>\n    <string name=\"install_to_user\">התקן למשתמש %s</string>\n    <string name=\"install_to_user_message\">רוצה להתקין %1$s למשתמש %2$s? מומלץ להתקין באופן ידני, כפיית התקנה באמצעות LSPosed עלולה לגרום לבעיות.</string>\n    <string name=\"expand\">להרחיב</string>\n    <string name=\"collapse\">התמוטטות</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">מטב מחדש</string>\n    <string name=\"compile_speed_msg\">ממטב…</string>\n    <string name=\"compile_done\">אופטימיזציה הושלמה.</string>\n    <string name=\"app_launch\">הרץ</string>\n    <string name=\"compile_failed\">אופטימיזציה נכשלה או שהערך המוחזר הוא ריק.</string>\n    <string name=\"compile_failed_with_info\">אופטימיזציה נכשלה: </string>\n    <string name=\"sort_by_name\">מיין על פי שם האפליקציה</string>\n    <string name=\"sort_by_package_name\">מיין על פי שם החבילה</string>\n    <string name=\"sort_by_install_time\">מיין על פי זמן ההתקנה</string>\n    <string name=\"sort_by_update_time\">מיין על פי זמן העדכון</string>\n    <string name=\"sort_reverse\">להפוך</string>\n    <string name=\"menu_show_system_apps\">אפליקציות מערכת</string>\n    <string name=\"menu_sort\">ממיין</string>\n    <string name=\"enable_module\">הפעל מודול</string>\n    <string name=\"no_scope_selected\">אתה לא בחרת שום אפליקציה. להמשיך?</string>\n    <string name=\"menu_show_games\">משחקים</string>\n    <string name=\"menu_show_modules\">מודולים</string>\n    <string name=\"menu_show_denylist\">רשימת דחיה</string>\n    <string name=\"failed_to_save_scope_list\">נכשל לשמור רשימת תחומים</string>\n    <string name=\"app_version\">גרסה: %1$s</string>\n    <string name=\"use_recommended\">מומלץ</string>\n    <string name=\"no_scope_selected_has_recommended\">אתה לא בחרת שום אפליקציה. לבחור אפליקציות מומלצות?</string>\n    <string name=\"use_recommended_message\">בחר אפליקציות מומלצות?</string>\n    <string name=\"module_is_not_activated_yet\">מודול LSPosed עדיין לא הופעל</string>\n    <string name=\"requested_by_module\">מומלץ</string>\n    <string name=\"update_available\">עדכון זמין: %1$s</string>\n    <string name=\"module_disabled_no_selection\">מודול %s בוטל מכיוון שלא נבחרה שום אפליקציה.</string>\n    <string name=\"android_framework\">מערכת Framework</string>\n    <string name=\"menu_backup_and_restore\">מגבה</string>\n    <string name=\"menu_backup\">גיבוי</string>\n    <string name=\"menu_restore\">שחזור</string>\n    <string name=\"force_stop\">אלץ עצירה</string>\n    <string name=\"force_stop_dlg_title\">אצץ עצירה?</string>\n    <string name=\"force_stop_dlg_text\">אם אתה תאלץ עצירה לאפליקציה, היא עלולה להתנהג בצורה לא רצויה.</string>\n    <string name=\"reboot_required\">הפעלה מחדש דרושה בכדי שהשינויים יכנסו לתוקף</string>\n    <string name=\"reboot\">הפעל מחדש</string>\n    <string name=\"menu_hide\">הסתר</string>\n    <string name=\"deny_list\">%s נמצא ברשימת הדחיה. ייתכן שהוא לא ייכנס לתוקף.</string>\n    <string name=\"deny_list_info\">ברשימת הדחיה</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">הצג באפליקציה אחרת</string>\n    <string name=\"module_app_info\">מידע על האפליקציה</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\n אין פה כלום</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">בטל verbose logs</string>\n    <string name=\"settings_disable_verbose_log_summary\">בקשת דיווח על בעיות לכלול יומנים מילוליים</string>\n    <string name=\"pure_black_dark_theme\">ערכת נושא שחור כהה</string>\n    <string name=\"pure_black_dark_theme_summary\">השתמש בערכת נושא שחור טהור אם ערכת נושא כהה מופעלת</string>\n    <string name=\"settings_group_theme\">ערכת נושא</string>\n    <string name=\"settings_backup_and_restore\">גיבוי ושחזור</string>\n    <string name=\"settings_backup_summery\">גיבוי רשימת מודולים ורשימות היקף.</string>\n    <string name=\"settings_restore_summery\">שחזר את רשימת המודולים ורשימות ההיקף.</string>\n    <string name=\"settings_backup\">גיבוי</string>\n    <string name=\"settings_backup_failed2\">גיבוי נכשל:\\n%s</string>\n    <string name=\"enable_documentui\">אנא הפעל את DocumentUI</string>\n    <string name=\"settings_restore\">שחזור</string>\n    <string name=\"settings_restore_failed2\">השחזור נכשל::\\n%s</string>\n    <string name=\"group_network\">רשת</string>\n    <string name=\"dns_over_http\">DNS על פני HTTPS</string>\n    <string name=\"dns_over_http_summary\">מעקף הרעלת DNS במדינות מסוימות</string>\n    <string name=\"theme_color\">צבע ערכת נושא</string>\n    <string name=\"theme_color_system\">צבע נושא המערכת</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">כפה על אפליקציות להציג סמלי מפעיל</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">לאחר Android 10, אפליקציות אינן מורשות להסתיר את סמלי המשגר שלהן. בטל את הלחצן הדו-מצבי כדי להפוך תכונת מערכת זו ללא זמינה.</string>\n    <string name=\"settings_group_system\">מערכת</string>\n    <string name=\"settings_language\">שפה</string>\n    <string name=\"settings_translation_contributors\">תורמים לתרגום</string>\n    <string name=\"settings_translation\">השתתף בתרגום</string>\n    <string name=\"settings_translation_summary\">עזור לנו לתרגם את %s לשפה שלך</string>\n    <string name=\"settings_create_shortcut_summary\">צור קיצור דרך שיכול לפתוח מנהל טפילי</string>\n    <string name=\"settings_shortcut_pinned_hint\">קיצור דרך הוצמד</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">מפעיל ברירת המחדל הנוכחי אינו תומך בקיצורי דרך</string>\n    <string name=\"settings_enable_status_notification\">הודעת סטטוס</string>\n    <string name=\"settings_enable_status_notification_summary\">הצג הודעה שיכולה לפתוח מנהל טפילי</string>\n    <string name=\"disable_status_notification_error\">אין קיצור דרך, לא ניתן להשבית הודעה</string>\n    <string name=\"settings_update_channel\">ערוץ עדכון</string>\n    <string name=\"update_channel_stable\">יציב</string>\n    <string name=\"update_channel_bate\">בטא</string>\n    <string name=\"update_channel_nightly\">בניה לילית</string>\n    <string name=\"settings_xposed_api_call_protection\">הגנה מפני קריאות Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">חסום קוד מודול שנטען באופן דינמי לשימוש ב-Xposed API, זה עשוי לשבור מודולים מסוימים אך להועיל לאבטחה</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">קרא אותי</string>\n    <string name=\"module_releases\">גרסאות</string>\n    <string name=\"module_information\">מידע</string>\n    <string name=\"module_information_homepage\">דף בית</string>\n    <string name=\"module_information_source_url\">קוד מקור</string>\n    <string name=\"module_information_collaborators\">מפתחים</string>\n    <string name=\"module_release_view_assets\">קבצים</string>\n    <string name=\"menu_open_in_browser\">פתח בדפדפן</string>\n    <string name=\"module_release_load_more\">הראה גרסאות ישנות</string>\n    <string name=\"module_release_no_more\">אין עוד גרסאות</string>\n    <string name=\"repo_load_failed\">נשכל לטעון מודול: %s</string>\n    <string name=\"sort_upgradable_first\">ניתן לשדרוג תחילה</string>\n    <string name=\"installed\">מוּתקָן</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d הורדה</item>\n        <item quantity=\"two\">%d הורדות</item>\n        <item quantity=\"many\">%d הורדות</item>\n        <item quantity=\"other\">%d הורדות</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">סאקורה</string>\n    <string name=\"color_red\">אדום</string>\n    <string name=\"color_pink\">ורוד</string>\n    <string name=\"color_purple\">סגול</string>\n    <string name=\"color_deep_purple\">סגול עמוק</string>\n    <string name=\"color_indigo\">אינדיגו</string>\n    <string name=\"color_blue\">כחול</string>\n    <string name=\"color_light_blue\">כחול בהיר</string>\n    <string name=\"color_cyan\">טורקיז</string>\n    <string name=\"color_teal\">ירוק כחלחל</string>\n    <string name=\"color_green\">ירוק</string>\n    <string name=\"color_light_green\">ירוק בהיר</string>\n    <string name=\"color_lime\">לימון</string>\n    <string name=\"color_yellow\">צהוב</string>\n    <string name=\"color_amber\">ענבר</string>\n    <string name=\"color_orange\">כתום</string>\n    <string name=\"color_deep_orange\">כתום עמוק</string>\n    <string name=\"color_brown\">חום</string>\n    <string name=\"color_blue_grey\">כחול אפור</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">概要</string>\n    <string name=\"Modules\">モジュール</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">%d 個のモジュールが有効です</item>\n    </plurals>\n    <string name=\"Logs\">ログ</string>\n    <string name=\"Settings\">設定</string>\n    <string name=\"feedback_or_suggestion\">フィードバックまたは提案</string>\n    <string name=\"About\">このアプリについて</string>\n    <string name=\"report_issue\">問題を報告</string>\n    <string name=\"module_repo\">リポジトリ</string>\n    <string name=\"module_repo_up_to_date\">全てのモジュールが最新です</string>\n    <string name=\"module_repo_published_time\">公開日時: %s</string>\n    <string name=\"module_repo_updated_time\">更新日時: %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d 個のモジュールが更新可能です</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[ソースコードを閲覧するにはこちら: %1$s<br/>チャンネルに参加するにはこちら: %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">yoshi818, a1678991, りお(koko0628)</string>\n    <string name=\"install\">インストール</string>\n    <string name=\"install_summary\">タップして LSPosed をインストールします</string>\n    <string name=\"not_installed\">インストールされていません</string>\n    <string name=\"not_install_summary\">LSPosed はインストールされていません</string>\n    <string name=\"activated\">有効化済み</string>\n    <string name=\"partial_activated\">部分的に有効化済み</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy が正しく読み込まれていません</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[一部の機能は動作しません。<br/>これを<a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>の開発者に報告してください。]]></string>\n    <string name=\"system_inject_fail_summary\">システムフレームワークへのパッチに失敗しました</string>\n    <string name=\"system_inject_fail\"><![CDATA[これは稀な現象であり、<b>Magisk</b>または低品質のMagiskモジュールが原因である可能性があります。<br/>LSPosed以外のMagiskモジュールを一時的に無効化してみるか、完全なログを開発者に送信してください。]]></string>\n    <string name=\"system_prop_incorrect_summary\">システム設定が正しくありません</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[必須のシステム設定が変更もしくは削除されています。<br/>モジュールが無効化される場合があります。]]></string>\n    <string name=\"need_update\">更新が必要です</string>\n    <string name=\"please_update_summary\">最新バージョンの LSPosed をインストールして下さい</string>\n    <string name=\"developer_warning_title\">モジュール開発者向けのヒント</string>\n    <string name=\"developer_warning_summary\">Android Studio でデプロイの最適化を無効にするか、「gradlew installDebug」コマンドを使用してインストールしてください。この操作を行わないとモジュールの apk は更新できません。</string>\n    <string name=\"info_api_version\">API バージョン</string>\n    <string name=\"info_framework_version\">フレームワークバージョン</string>\n    <string name=\"info_manager_package_name\">マネージャーパッケージ名</string>\n    <string name=\"info_system_version\">システムバージョン</string>\n    <string name=\"info_device\">デバイス</string>\n    <string name=\"info_system_abi\">システム ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex 最適化ラッパー</string>\n    <string name=\"enabled\">有効</string>\n    <string name=\"not_enabled\">無効</string>\n    <string name=\"supported\">対応</string>\n    <string name=\"unsupported\">非対応</string>\n    <string name=\"android_version_unsatisfied\">Android のバージョンが適合しません</string>\n    <string name=\"crashed\">クラッシュしました</string>\n    <string name=\"mount_failed\">マウントに失敗しました</string>\n    <string name=\"selinux_permissive\">SELinux は Permissive です</string>\n    <string name=\"sepolicy_incorrect\">SELinux のポリシーが正しくありません</string>\n    <string name=\"update_lsposed\">LSPosed を更新</string>\n    <string name=\"update_lsposed_msg\">LSPosed を更新してもよろしいですか？更新の完了後、このデバイスは再起動します</string>\n    <string name=\"info_copied\">クリップボードにコピーされました</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPosed へようこそ</string>\n    <string name=\"parasitic_welcome_summary\">パラサイトマネージャーを使用しています。これにより、ショートカットを作成したり、通知から開いたりできます。</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">通知から開くことができるパラサイトマネージャーを使用しています。</string>\n    <string name=\"create_shortcut\">ショートカットを作成</string>\n    <string name=\"never_show\">再度表示しない</string>\n    <string name=\"app_welcome\">パラサイトマネージャーを使用することを推奨します</string>\n    <string name=\"app_welcome_summary\">LSPosed は検出を回避するためのシステムパラサイトに対応し、通知からパラサイトマネージャーを開くことができるようになりました。現在のアプリケーションをアンインストールすることをおすすめします。</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">保存</string>\n    <string name=\"nav_item_logs_verbose\">詳細ログ</string>\n    <string name=\"nav_item_logs_module\">モジュールログ</string>\n    <string name=\"logs_saving\">ログを保存しています。お待ちください</string>\n    <string name=\"logs_saved\">ログを保存しました</string>\n    <string name=\"logs_save_failed2\">保存に失敗しました:\\n%s</string>\n    <string name=\"menuClearLog\">ログを消去</string>\n    <string name=\"logs_cleared\">ログの消去に成功しました。</string>\n    <string name=\"scroll_top\">ログの先頭行にスクロール</string>\n    <string name=\"loading\">読み込み中…</string>\n    <string name=\"scroll_bottom\">一番下までスクロール</string>\n    <string name=\"menuReload\">再読み込み</string>\n    <string name=\"logs_clear_failed_2\">ログを消去できませんでした</string>\n    <string name=\"menu_enable_word_wrap\">単語を折り返す</string>\n    <string name=\"enabled_verbose_log\">詳細ログは有効です</string>\n    <string name=\"disabled_verbose_log\">詳細ログは無効です</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(説明はありません)</string>\n    <string name=\"warning_xposed_min_version\">このモジュールは新しいバージョンの Xposed (%d) が必要なので有効化できません</string>\n    <string name=\"warning_target_version_higher\">このモジュールは新しい Xposed バージョン (%d) 用に設計されているため、一部の機能が動作しない可能性があります</string>\n    <string name=\"no_min_version_specified\">このモジュールは必要な Xposed のバージョンを指定していません。</string>\n    <string name=\"warning_min_version_too_low\">このモジュールは Xposed バージョン %1$d 用に作成されましたが、バージョン %2$dでの互換性のない変更により無効化されました。</string>\n    <string name=\"warning_installed_on_external_storage\">このモジュールは SD カードにインストールされているため読み込むことができません。内部ストレージに移動してください。</string>\n    <string name=\"module_uninstall\">アンインストール</string>\n    <string name=\"module_settings\">モジュール設定</string>\n    <string name=\"view_in_repo\">リポジトリで表示</string>\n    <string name=\"module_uninstall_message\">このモジュールをアンインストールしますか？</string>\n    <string name=\"module_uninstalled\">「%1$s」をアンインインストールしました</string>\n    <string name=\"module_uninstall_failed\">アンインストールに成功</string>\n    <string name=\"add_module_to_user\">ユーザへモジュールを追加</string>\n    <string name=\"module_installed\">%1$s をユーザー %2$s へ追加</string>\n    <string name=\"module_install_failed\">モジュールの追加に失敗しました</string>\n    <string name=\"install_to_user\">ユーザー %s へインストール</string>\n    <string name=\"install_to_user_message\">ユーザー %2$s へ %1$s をインストールしますか？LSPosed 経由での強制インストールは問題が発生する場合があるため、手動でインストールすることをおすすめします。</string>\n    <string name=\"expand\">開く</string>\n    <string name=\"collapse\">閉じる</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">再最適化</string>\n    <string name=\"compile_speed_msg\">最適化中…</string>\n    <string name=\"compile_done\">最適化が完了しました</string>\n    <string name=\"app_launch\">起動</string>\n    <string name=\"compile_failed\">最適化に失敗しました: 戻り値が空です</string>\n    <string name=\"compile_failed_with_info\">最適化に失敗しました: </string>\n    <string name=\"sort_by_name\">アプリ名</string>\n    <string name=\"sort_by_package_name\">パッケージ名</string>\n    <string name=\"sort_by_install_time\">インストール日時</string>\n    <string name=\"sort_by_update_time\">更新日時</string>\n    <string name=\"sort_reverse\">逆順</string>\n    <string name=\"menu_show_system_apps\">システムアプリ</string>\n    <string name=\"menu_sort\">並び順</string>\n    <string name=\"enable_module\">モジュールを有効化</string>\n    <string name=\"no_scope_selected\">アプリが選択されていません。よろしいですか？</string>\n    <string name=\"menu_show_games\">ゲーム</string>\n    <string name=\"menu_show_modules\">モジュール</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">スコープリストの保存に失敗しました</string>\n    <string name=\"app_version\">バージョン: %1$s</string>\n    <string name=\"menu_select\">選択</string>\n    <string name=\"use_recommended\">推奨</string>\n    <string name=\"no_scope_selected_has_recommended\">アプリが選択されていません。おすすめのアプリを選択しますか？</string>\n    <string name=\"use_recommended_message\">おすすめのアプリを選択しますか？</string>\n    <string name=\"menu_select_all\">すべて</string>\n    <string name=\"menu_select_none\">なし</string>\n    <string name=\"menu_auto_include\">自動的に含む</string>\n    <string name=\"module_is_not_activated_yet\">Xposed モジュールが有効化されていません</string>\n    <string name=\"requested_by_module\">推奨</string>\n    <string name=\"update_available\">アップデートが利用可能です: %1$s</string>\n    <string name=\"module_disabled_no_selection\">アプリが選択されていないため、モジュール「%s」は無効になっています。</string>\n    <string name=\"android_framework\">システムフレームワーク</string>\n    <string name=\"menu_backup_and_restore\">バックアップ</string>\n    <string name=\"menu_backup\">バックアップ</string>\n    <string name=\"menu_restore\">復元</string>\n    <string name=\"force_stop\">強制停止</string>\n    <string name=\"force_stop_dlg_title\">強制停止しますか？</string>\n    <string name=\"force_stop_dlg_text\">アプリを強制停止すると、アプリが動作しなくなる可能性があります。</string>\n    <string name=\"reboot_required\">この設定を適用するには再起動が必要です</string>\n    <string name=\"reboot\">再起動</string>\n    <string name=\"menu_hide\">非表示</string>\n    <string name=\"deny_list\">%s が DenyList に登録されています。効果がないかもしれません。</string>\n    <string name=\"deny_list_info\">DenyList に登録されています</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">他のアプリで表示</string>\n    <string name=\"module_app_info\">アプリの情報</string>\n    <string name=\"list_empty\">¯\\\\_(ツ)_\\/¯\\nリストは空です</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">フレームワーク</string>\n    <string name=\"settings_disable_verbose_log\">詳細ログの無効化</string>\n    <string name=\"settings_disable_verbose_log_summary\">問題を報告する際は、詳細なログを含めるようにしてください</string>\n    <string name=\"settings_enable_log_watchdog\">ログウォッチドッグを有効化</string>\n    <string name=\"settings_enable_log_watchdog_summary\">LSPosed のログウォッチドッグはシステムプロパティを変更し、LSPosed を検出するために悪用される可能性があります</string>\n    <string name=\"pure_black_dark_theme\">黒のダークテーマ</string>\n    <string name=\"pure_black_dark_theme_summary\">ダークテーマが有効になっている場合は、ピュアブラックテーマを使用します</string>\n    <string name=\"settings_group_theme\">テーマ</string>\n    <string name=\"settings_backup_and_restore\">バックアップと復元</string>\n    <string name=\"settings_backup_summery\">モジュールリストとスコープリストをバックアップします</string>\n    <string name=\"settings_restore_summery\">モジュールリストとスコープリストを復元します</string>\n    <string name=\"settings_backup\">バックアップ</string>\n    <string name=\"settings_backup_failed2\">バックアップに失敗しました:\\n%s</string>\n    <string name=\"enable_documentui\">DocumentUI を有効にしてください</string>\n    <string name=\"settings_restore\">復元</string>\n    <string name=\"settings_restore_failed2\">復元に失敗しました:\\n%s</string>\n    <string name=\"group_network\">ネットワーク</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">一部の国向けの DNS ポイズニング回避策</string>\n    <string name=\"theme_color\">テーマカラー</string>\n    <string name=\"theme_color_system\">システムテーマカラー</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">ランチャーアイコンを強制的に表示</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10 以降、アプリはランチャーアイコンを隠すことができなくなりました。このシステム機能を無効にするには、トグルをオフにしてください。</string>\n    <string name=\"settings_group_system\">システム</string>\n    <string name=\"settings_language\">言語</string>\n    <string name=\"settings_translation_contributors\">翻訳貢献者</string>\n    <string name=\"settings_translation\">翻訳に貢献</string>\n    <string name=\"settings_translation_summary\">%s の翻訳にご協力ください</string>\n    <string name=\"settings_create_shortcut_summary\">パラサイトマネージャーを開くことができるショートカットを作成します</string>\n    <string name=\"settings_shortcut_pinned_hint\">ショートカットのピン留め</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">現在のデフォルトランチャーはショートカットのピン留めをサポートしていません</string>\n    <string name=\"settings_enable_status_notification\">ステータス通知</string>\n    <string name=\"settings_enable_status_notification_summary\">パラサイトマネージャーを開くことができる通知を表示します</string>\n    <string name=\"disable_status_notification_error\">ショートカットがないため、通知を無効にできません</string>\n    <string name=\"settings_update_channel\">更新チャンネル</string>\n    <string name=\"update_channel_stable\">安定版</string>\n    <string name=\"update_channel_bate\">ベータ版</string>\n    <string name=\"update_channel_nightly\">ナイトリービルド</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API の呼び出し保護</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">動的にロードされたモジュールコードが Xposed API を使用するのをブロックします。これにより一部のモジュールが動作しなくなる可能性がありますが、セキュリティが向上します。</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">リリース</string>\n    <string name=\"module_information\">情報</string>\n    <string name=\"module_information_homepage\">ホームページ</string>\n    <string name=\"module_information_source_url\">ソースコード</string>\n    <string name=\"module_information_collaborators\">協力者</string>\n    <string name=\"module_release_view_assets\">アセット</string>\n    <string name=\"menu_open_in_browser\">ブラウザで表示</string>\n    <string name=\"module_release_load_more\">以前のバージョンを表示</string>\n    <string name=\"module_release_no_more\">これ以上のリリースはありません</string>\n    <string name=\"repo_load_failed\">モジュールリポジトリの読み込みに失敗しました: %s</string>\n    <string name=\"sort_upgradable_first\">更新があるモジュールを先頭に表示</string>\n    <string name=\"installed\">インストール済み</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d 件のダウンロード</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">桜色</string>\n    <string name=\"color_red\">赤</string>\n    <string name=\"color_pink\">ピンク</string>\n    <string name=\"color_purple\">紫</string>\n    <string name=\"color_deep_purple\">深紫色</string>\n    <string name=\"color_indigo\">藍色</string>\n    <string name=\"color_blue\">青</string>\n    <string name=\"color_light_blue\">水色</string>\n    <string name=\"color_cyan\">シアン</string>\n    <string name=\"color_teal\">青緑</string>\n    <string name=\"color_green\">緑</string>\n    <string name=\"color_light_green\">ライトグリーン</string>\n    <string name=\"color_lime\">ライム</string>\n    <string name=\"color_yellow\">黄色</string>\n    <string name=\"color_amber\">アンバー</string>\n    <string name=\"color_orange\">オレンジ色</string>\n    <string name=\"color_deep_orange\">ディープオレンジ</string>\n    <string name=\"color_brown\">茶色</string>\n    <string name=\"color_blue_grey\">ブルーグレー</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">개요</string>\n    <string name=\"Modules\">모듈</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">모듈 %d개가 활성화됨</item>\n    </plurals>\n    <string name=\"Logs\">로그</string>\n    <string name=\"Settings\">설정</string>\n    <string name=\"feedback_or_suggestion\">피드백 혹은 제안</string>\n    <string name=\"About\">정보</string>\n    <string name=\"report_issue\">문제 보고</string>\n    <string name=\"module_repo\">저장소</string>\n    <string name=\"module_repo_up_to_date\">모든 모듈이 최신 버전입니다</string>\n    <string name=\"module_repo_published_time\">%s 에 배포됨</string>\n    <string name=\"module_repo_updated_time\">%s에 업데이트됨</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d개의 모듈이 업데이트 가능합니다</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[%1$s에서 소스 코드 보기<br/>%2$s 채널에 가입하세요]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">green1052</string>\n    <string name=\"install\">설치</string>\n    <string name=\"install_summary\">LSPosed를 설치하려면 누르세요</string>\n    <string name=\"not_installed\">설치되지 않음</string>\n    <string name=\"not_install_summary\">LSPosed가 설치되지 않았습니다</string>\n    <string name=\"activated\">활성화됨</string>\n    <string name=\"partial_activated\">부분적으로 활성화됨</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy가 제대로 로드되지 않았습니다</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[몇몇 기능은 작동하지 않을 수도 있습니다.<br/>오류를 <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk 개발자</b></a>에게 신고해주세요]]></string>\n    <string name=\"system_inject_fail_summary\">System Framework 삽입 실패</string>\n    <string name=\"system_inject_fail\"><![CDATA[이는 드물게 발생하며, <b>Magisk</b> 또는 일부 Magisk 모듈 때문일 수 있습니다.<br/>Riru 및 LSPosed 이외의 Magisk 모듈을 비활성화 하거나 전체 로그를 개발자에게 제출하세요.]]></string>\n    <string name=\"system_prop_incorrect_summary\">시스템 속성이 잘못됨</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[일부 필수 시스템 속성이 삭제 또는 수정되었습니다.<br/>모듈은 가끔 무효화될 수 있습니다.]]></string>\n    <string name=\"need_update\">업데이트가 필요합니다</string>\n    <string name=\"please_update_summary\">최신 버전으로 LSPosed를 설치해 주세요</string>\n    <string name=\"developer_warning_title\">모듈 개발자를 위한 팁</string>\n    <string name=\"developer_warning_summary\">Android Studio 에서 배포 최적화를 비활성화 하거나, 설치할때 \\'gradlew installDebug\\' 명령어를 사용해주세요. 그렇지 않으면 모듈 APK 가 업데이트되지 않을 것입니다.</string>\n    <string name=\"info_api_version\">API 버전</string>\n    <string name=\"info_framework_version\">Framework 버전</string>\n    <string name=\"info_manager_package_name\">관리자 패키지 이름</string>\n    <string name=\"info_system_version\">System 버전</string>\n    <string name=\"info_device\">장치</string>\n    <string name=\"info_system_abi\">시스템 ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex 옵티마이저 래퍼</string>\n    <string name=\"enabled\">사용</string>\n    <string name=\"not_enabled\">사용 안 함</string>\n    <string name=\"supported\">지원</string>\n    <string name=\"unsupported\">지원되지 않음</string>\n    <string name=\"android_version_unsatisfied\">안드로이드 버전이 지원되지 않습니다</string>\n    <string name=\"crashed\">충돌</string>\n    <string name=\"mount_failed\">마운트 실패</string>\n    <string name=\"selinux_permissive\">SELinux가 허용됩니다</string>\n    <string name=\"sepolicy_incorrect\">SELinux 정책이 잘못되었습니다</string>\n    <string name=\"update_lsposed\">LSPosed 업데이트</string>\n    <string name=\"update_lsposed_msg\">LSPosed를 업데이트 하시겠습니까? 업데이트 완료 후에 재부팅합니다</string>\n    <string name=\"info_copied\">클립보드에 복사됨</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPosed에 오신 것을 환영합니다</string>\n    <string name=\"parasitic_welcome_summary\">바로 가기를 만들거나 알림에서 계속 열 수 있는 기생 관리자를 사용하고 있습니다.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">알림에서 열 수 있는 내부 관리자를 사용 중 입니다.</string>\n    <string name=\"create_shortcut\">바로 가기 만들기</string>\n    <string name=\"never_show\">표시 안 함</string>\n    <string name=\"app_welcome\">기생 매니저 추천</string>\n    <string name=\"app_welcome_summary\">LSPosed는 이제 탐지를 피하기 위해 시스템 기생을 지원하며 알림에서 기생 관리자를 열 수 있습니다. 현재 응용 프로그램을 제거하는 것이 좋습니다.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">저장</string>\n    <string name=\"nav_item_logs_verbose\">자세한 로그</string>\n    <string name=\"nav_item_logs_module\">모듈 로그</string>\n    <string name=\"logs_saving\">로그를 저장중입니다, 기다려주세요</string>\n    <string name=\"logs_saved\">로그가 저장되었습니다</string>\n    <string name=\"logs_save_failed2\">저장 실패:\\n%s</string>\n    <string name=\"menuClearLog\">로그 지우기</string>\n    <string name=\"logs_cleared\">로그를 성공적으로 지웠습니다.</string>\n    <string name=\"scroll_top\">맨 위로 스크롤</string>\n    <string name=\"loading\">로딩 중…</string>\n    <string name=\"scroll_bottom\">아래로 스크롤</string>\n    <string name=\"menuReload\">리로드</string>\n    <string name=\"logs_clear_failed_2\">로그를 지우지 못했습니다.</string>\n    <string name=\"menu_enable_word_wrap\">줄 바꿈</string>\n    <string name=\"enabled_verbose_log\">자세한 로그 사용</string>\n    <string name=\"disabled_verbose_log\">자세한 로그 비활성화</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(설명 없음)</string>\n    <string name=\"warning_xposed_min_version\">이 모듈에는 최신 LSPosed (%d) 버전이 필요하므로 활성화할 수 없습니다.</string>\n    <string name=\"warning_target_version_higher\">이 모듈은 더 새로운 Xposed 버전 (%d) 에서 만들어졌고 따라서 몇몇 기능들이 작동하지 않을 수도 있습니다</string>\n    <string name=\"no_min_version_specified\">이 모듈에서는 필요한 LSPosed 버전을 지정하지 않습니다.</string>\n    <string name=\"warning_min_version_too_low\">이 모듈은 LSPosed 버전 %1$d에 대해 생성되었지만 버전 %2$d에서 호환되지 않는 변경으로 인해 비활성화되었습니다.</string>\n    <string name=\"warning_installed_on_external_storage\">이 모듈은 SD 카드에 설치되어 있으므로 로드할 수 없습니다. 내부 스토리지로 이동하십시오.</string>\n    <string name=\"module_uninstall\">제거</string>\n    <string name=\"module_settings\">모듈 설정</string>\n    <string name=\"view_in_repo\">저장소에서 보기</string>\n    <string name=\"module_uninstall_message\">이 모듈을 제거하시겠습니까?</string>\n    <string name=\"module_uninstalled\">%1$s 제거됨</string>\n    <string name=\"module_uninstall_failed\">제거 실패</string>\n    <string name=\"add_module_to_user\">사용자에게 모듈 추가</string>\n    <string name=\"module_installed\">%2$s 사용자에 %1$s 추가</string>\n    <string name=\"module_install_failed\">모듈 추가 실패</string>\n    <string name=\"install_to_user\">%s 사용자에게 설치</string>\n    <string name=\"install_to_user_message\">%2$s 사용자에게 %1$s을(를) 설치하시겠습니까? 수동으로 설치하는 것이 좋습니다. LSPosed를 통해 강제로 설치하면 문제가 발생할 수 있습니다.</string>\n    <string name=\"expand\">펼치기</string>\n    <string name=\"collapse\">접기</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">다시 최적화</string>\n    <string name=\"compile_speed_msg\">최적화 중…</string>\n    <string name=\"compile_done\">최적화 완료</string>\n    <string name=\"app_launch\">실행</string>\n    <string name=\"compile_failed\">최적화에 실패했거나 반환 값이 비어 있습니다.</string>\n    <string name=\"compile_failed_with_info\">최적화 실패: </string>\n    <string name=\"sort_by_name\">애플리케이션 이름</string>\n    <string name=\"sort_by_package_name\">패키지 이름</string>\n    <string name=\"sort_by_install_time\">설치 시간</string>\n    <string name=\"sort_by_update_time\">업데이트 시간</string>\n    <string name=\"sort_reverse\">역순</string>\n    <string name=\"menu_show_system_apps\">시스템 앱</string>\n    <string name=\"menu_sort\">정렬</string>\n    <string name=\"enable_module\">모듈 활성화</string>\n    <string name=\"no_scope_selected\">앱을 선택하지 않았습니다. 계속하시겠습니까?</string>\n    <string name=\"menu_show_games\">게임</string>\n    <string name=\"menu_show_modules\">모듈</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">범위 목록 저장에 실패했습니다.</string>\n    <string name=\"app_version\">버전: %1$s</string>\n    <string name=\"use_recommended\">권장</string>\n    <string name=\"no_scope_selected_has_recommended\">앱을 선택하지 않았습니다. 권장 앱을 선택하시겠습니까?</string>\n    <string name=\"use_recommended_message\">권장 앱을 선택하시겠습니까?</string>\n    <string name=\"module_is_not_activated_yet\">LSPosed 모듈이 아직 활성화되지 않았습니다.</string>\n    <string name=\"requested_by_module\">권장</string>\n    <string name=\"update_available\">업데이트 가능: %1$s</string>\n    <string name=\"module_disabled_no_selection\">앱을 선택하지 않았기 때문에 %s 모듈이 비활성화되었습니다.</string>\n    <string name=\"android_framework\">시스템 프레임워크</string>\n    <string name=\"menu_backup_and_restore\">백업</string>\n    <string name=\"menu_backup\">백업</string>\n    <string name=\"menu_restore\">복원</string>\n    <string name=\"force_stop\">강제 중지</string>\n    <string name=\"force_stop_dlg_title\">강제 중지?</string>\n    <string name=\"force_stop_dlg_text\">앱을 강제로 중지하면 잘못된 동작이 발생할 수 있습니다.</string>\n    <string name=\"reboot_required\">이 변경 내용을 적용하려면 재부팅해야 합니다.</string>\n    <string name=\"reboot\">재부팅</string>\n    <string name=\"menu_hide\">숨김</string>\n    <string name=\"deny_list\">%s 은 거부 목록에 있습니다. 적용되지 않을 수 있습니다.</string>\n    <string name=\"deny_list_info\">차단 목록에서</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">다른 앱으로 보기</string>\n    <string name=\"module_app_info\">앱 정보</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\n아무것도 없음</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">프레임워크</string>\n    <string name=\"settings_disable_verbose_log\">상세한 로그 비활성화</string>\n    <string name=\"settings_disable_verbose_log_summary\">자세한 로그를 포함한 오류 신고 요청</string>\n    <string name=\"pure_black_dark_theme\">블랙 다크 테마</string>\n    <string name=\"pure_black_dark_theme_summary\">다크 테마가 활성화된 경우 순수 검은색 테마를 사용합니다.</string>\n    <string name=\"settings_group_theme\">테마</string>\n    <string name=\"settings_backup_and_restore\">백업 및 복원</string>\n    <string name=\"settings_backup_summery\">모듈 목록과 스코프 목록을 백업합니다.</string>\n    <string name=\"settings_restore_summery\">모듈 목록과 스코프 목록을 복원합니다.</string>\n    <string name=\"settings_backup\">백업</string>\n    <string name=\"settings_backup_failed2\">백업 실패:\\n%s</string>\n    <string name=\"enable_documentui\">DocumentUI를 활성화하십시오</string>\n    <string name=\"settings_restore\">복원</string>\n    <string name=\"settings_restore_failed2\">복원 실패:\\n%s</string>\n    <string name=\"group_network\">네트워크</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">일부 국가의 DNS 포이즈닝 문제를 해결합니다</string>\n    <string name=\"theme_color\">테마 색</string>\n    <string name=\"theme_color_system\">System 강조 색</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">앱에서 시작 프로그램 아이콘 표시</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10 이후 앱(특히 Xposed 모듈)은 시작 프로그램 아이콘을 숨길 수 없습니다. 이 기능을 비활성화하려면 토글을 끄십시오.</string>\n    <string name=\"settings_group_system\">체계</string>\n    <string name=\"settings_language\">언어</string>\n    <string name=\"settings_translation_contributors\">번역 기여자</string>\n    <string name=\"settings_translation\">번역 참여</string>\n    <string name=\"settings_translation_summary\">%s를 귀하의 언어로 번역하는 데 도움을 주세요.</string>\n    <string name=\"settings_create_shortcut_summary\">내부 관리자를 열 수 있는 바로가기 생성</string>\n    <string name=\"settings_shortcut_pinned_hint\">바로 가기 고정</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">현재 기본 런쳐는 핀 바로가기를 지원하지 않습니다</string>\n    <string name=\"settings_enable_status_notification\">상태 알림</string>\n    <string name=\"settings_enable_status_notification_summary\">기생 관리자를 열 수 있는 알림 표시</string>\n    <string name=\"disable_status_notification_error\">바로 가기 없음, 알림을 비활성화할 수 없음</string>\n    <string name=\"settings_update_channel\">업데이트 채널</string>\n    <string name=\"update_channel_stable\">안정</string>\n    <string name=\"update_channel_bate\">베타</string>\n    <string name=\"update_channel_nightly\">야간 빌드</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API 호출 보호</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Xposed API를 사용하기 위해 로드된 모듈 코드를 동적으로 차단하면 일부 모듈이 손상될 수 있지만 보안에 도움이 됩니다.</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">읽어보기</string>\n    <string name=\"module_releases\">릴리스</string>\n    <string name=\"module_information\">정보</string>\n    <string name=\"module_information_homepage\">홈페이지</string>\n    <string name=\"module_information_source_url\">소스 코드</string>\n    <string name=\"module_information_collaborators\">기여자</string>\n    <string name=\"module_release_view_assets\">자산</string>\n    <string name=\"menu_open_in_browser\">브라우저에서 열기</string>\n    <string name=\"module_release_load_more\">이전 버전 표시</string>\n    <string name=\"module_release_no_more\">더 이상 출시되지 않음</string>\n    <string name=\"repo_load_failed\">모듈 저장소를 로드하지 못함: %s</string>\n    <string name=\"sort_upgradable_first\">먼저 업그레이드 가능</string>\n    <string name=\"installed\">설치됨</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d 다운로드</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">벚꽃</string>\n    <string name=\"color_red\">빨강</string>\n    <string name=\"color_pink\">분홍</string>\n    <string name=\"color_purple\">보라</string>\n    <string name=\"color_deep_purple\">짙은 보라</string>\n    <string name=\"color_indigo\">군청</string>\n    <string name=\"color_blue\">파랑</string>\n    <string name=\"color_light_blue\">밝은 파랑</string>\n    <string name=\"color_cyan\">청록</string>\n    <string name=\"color_teal\">암청</string>\n    <string name=\"color_green\">초록</string>\n    <string name=\"color_light_green\">연두</string>\n    <string name=\"color_lime\">라임</string>\n    <string name=\"color_yellow\">노랑</string>\n    <string name=\"color_amber\">호박</string>\n    <string name=\"color_orange\">주황색</string>\n    <string name=\"color_deep_orange\">짙은 주황</string>\n    <string name=\"color_brown\">갈색</string>\n    <string name=\"color_blue_grey\">청회색</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ku/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">کورتەی گشتی</string>\n    <string name=\"Modules\">مۆدیوولەکان</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d مۆدیول چالاک کراوە</item>\n        <item quantity=\"other\">%d مۆدیول چالاک کراوە</item>\n    </plurals>\n    <string name=\"Logs\">لۆگەکان</string>\n    <string name=\"Settings\">ڕێکخستنەکان</string>\n    <string name=\"feedback_or_suggestion\">فیدباک یان پێشنیار</string>\n    <string name=\"About\">دەربارە</string>\n    <string name=\"report_issue\">پرسی ڕاپۆرت</string>\n    <string name=\"module_repo\">کۆگا</string>\n    <string name=\"module_repo_up_to_date\">Hemî modulên nûjen</string>\n    <string name=\"module_repo_published_time\">Di %sde hate weşandin</string>\n    <string name=\"module_repo_updated_time\">Di %sde hate nûve kirin</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d module nûvekirin</item>\n        <item quantity=\"other\">%d modulên nûvekirî</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Koda çavkaniyê li %1$s<br/>bibînin Tevlî %2$s kanala me bibin]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Lêkirin</string>\n    <string name=\"install_summary\">Ji bo sazkirina LSPosed bikirtînin</string>\n    <string name=\"not_installed\">Ne hatiye sazkirin</string>\n    <string name=\"not_install_summary\">LSPosed nayê Sazkirin</string>\n    <string name=\"activated\">Çalak kirin</string>\n    <string name=\"partial_activated\">Qismî aktîf kirin</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy bi rêkûpêk nayê barkirin</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Hin taybetmendî dê nexebitin.<br/>Ji kerema xwe vê yekê ji <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> pêşdebir re ragihînin.]]></string>\n    <string name=\"system_inject_fail_summary\">Derzkirina Çarçoveya Pergalê têk çû</string>\n    <string name=\"system_inject_fail\"><![CDATA[Ev kêm e û dibe ku ji hêla <b>Magisk</b> an hin modulên Magisk-a kêm-kalîteyê ve çêbibe.<br/>Ji kerema xwe hewl bidin ku modulên Magisk ji bilî Riru û LSPosed neçalak bikin an jî têketinek tevahî ji pêşdebiran re bişînin.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Pêşniyara pergalê xelet e</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Hin taybetmendiyên pergalê yên pêwîst jêbirin an guherandin.<br/>Dibe ku modul carinan betal bibin.]]></string>\n    <string name=\"need_update\">Pêdivî ye ku nûve bike</string>\n    <string name=\"please_update_summary\">Ji kerema xwe guhertoya herî dawî ya LSPosed saz bikin</string>\n    <string name=\"info_api_version\">Guhertoya API</string>\n    <string name=\"info_framework_version\">Guhertoya çarçoveyê</string>\n    <string name=\"info_manager_package_name\">Navê pakêta rêveberê</string>\n    <string name=\"info_system_version\">Guhertoya pergalê</string>\n    <string name=\"info_device\">Sazî</string>\n    <string name=\"info_system_abi\">Pergala ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Enabled</string>\n    <string name=\"not_enabled\">Ne çalak kirin</string>\n    <string name=\"supported\">Piştgirî kirin</string>\n    <string name=\"unsupported\">Piştgirî nekirin</string>\n    <string name=\"android_version_unsatisfied\">Guhertoya Android-ê ne razî ye</string>\n    <string name=\"crashed\">Qeza kirin</string>\n    <string name=\"mount_failed\">Çiya têk çû</string>\n    <string name=\"selinux_permissive\">SELinux destûr e</string>\n    <string name=\"sepolicy_incorrect\">Siyaseta SELinux nerast e</string>\n    <string name=\"update_lsposed\">LSPosed nûve bikin</string>\n    <string name=\"update_lsposed_msg\">Piştrast bike ku LSPosed nûve bike? Ev cîhaz dê piştî qedandina nûvekirinê ji nû ve dest pê bike</string>\n    <string name=\"info_copied\">Li clipboardê hate kopî kirin</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Hûn bi xêr hatin LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Hûn rêveberê parazît bikar tînin, ku dikare kurtebirê biafirîne an hîn jî ji ragihandinê vebe.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Hûn rêveberê parazît bikar tînin, ku dikare ji ragihandinê vebe.</string>\n    <string name=\"create_shortcut\">Kurtenivîsê çêbikin</string>\n    <string name=\"never_show\">Qet nîşan nedin</string>\n    <string name=\"app_welcome\">Rêvebirê Parazît Pêşniyar kirin</string>\n    <string name=\"app_welcome_summary\">LSPosed naha parazîtkirina pergalê piştgirî dike da ku ji tespîtê dûr bixe, hûn dikarin rêveberê parazît ji ragihandinê vekin. Tê pêşniyar kirin ku serîlêdana heyî jêbirin.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Rizgarkirin</string>\n    <string name=\"nav_item_logs_verbose\">Têketinên Verbose</string>\n    <string name=\"nav_item_logs_module\">Têketinên Modulan</string>\n    <string name=\"logs_saving\">پاشەکەوتکردنی لۆگ، تکایە چاوەڕوان بن</string>\n    <string name=\"logs_saved\">Têketin xilas kirin</string>\n    <string name=\"logs_save_failed2\">Sazkirin bi ser neket:\\n%s</string>\n    <string name=\"menuClearLog\">Naha têketinê paqij bike</string>\n    <string name=\"logs_cleared\">Têketin bi serkeftî hate paqij kirin.</string>\n    <string name=\"scroll_top\">Scroll to top</string>\n    <string name=\"loading\">Barkirin…</string>\n    <string name=\"scroll_bottom\">Scroll to bottom</string>\n    <string name=\"menuReload\">Ji nû ve barkirin</string>\n    <string name=\"logs_clear_failed_2\">Paqijkirina têketinê bi ser neket</string>\n    <string name=\"menu_enable_word_wrap\">Peyv Wrap</string>\n    <string name=\"enabled_verbose_log\">Têketinê bi lêker vekir</string>\n    <string name=\"disabled_verbose_log\">Têketinê bi devkî neçalak bû</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(bê şirove nehat dayîn)</string>\n    <string name=\"warning_xposed_min_version\">Vê modulê guhertoyek nû ya Xposed (%d) hewce dike û ji ber vê yekê nayê çalak kirin</string>\n    <string name=\"warning_target_version_higher\">Ev modul ji bo guhertoyek nû ya Xposed (%d) hatî çêkirin û ji ber vê yekê dibe ku hin fonksiyon nexebitin</string>\n    <string name=\"no_min_version_specified\">Ev modul guhertoya Xposed ya ku jê re hewce dike diyar nake.</string>\n    <string name=\"warning_min_version_too_low\">Ev modul ji bo guhertoya Xposed %1$dhate afirandin, lê ji ber guheztinên nelihev ên di guhertoya %2$d-ê de, ew hate asteng kirin.</string>\n    <string name=\"warning_installed_on_external_storage\">Ev modul nikare were barkirin ji ber ku ew li ser qerta SD-ê hatî saz kirin, ji kerema xwe wê biguhezînin hilana hundurîn</string>\n    <string name=\"module_uninstall\">Rakirin</string>\n    <string name=\"module_settings\">Mîhengên Modulê</string>\n    <string name=\"view_in_repo\">Di Repo de bibînin</string>\n    <string name=\"module_uninstall_message\">Ma hûn dixwazin vê modulê rakin?</string>\n    <string name=\"module_uninstalled\">Rakir %1$s</string>\n    <string name=\"module_uninstall_failed\">Rakirina neserketî ye</string>\n    <string name=\"add_module_to_user\">Modulê li bikarhênerê zêde bikin</string>\n    <string name=\"module_installed\">%1$s ji bikarhêner %2$sre zêde kirin</string>\n    <string name=\"module_install_failed\">Zêdekirina modulê têk çû</string>\n    <string name=\"install_to_user\">Ji bikarhêner %sre saz bike</string>\n    <string name=\"install_to_user_message\">Dixwazin %1$s ji bikarhêner %2$sre saz bikin? Tête pêşniyar kirin ku bi destan saz bikin, zordariya sazkirinê bi riya LSPosed dibe ku bibe sedema pirsgirêkan.</string>\n    <string name=\"expand\">firehkirin</string>\n    <string name=\"collapse\">jiberhevketin</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Ji nû ve xweşbîn bikin</string>\n    <string name=\"compile_speed_msg\">Optimîzekirin…</string>\n    <string name=\"compile_done\">Optimîzasyon qediya</string>\n    <string name=\"app_launch\">Dest pê bike</string>\n    <string name=\"compile_failed\">Optimîzasyon têk çû: nirxa vegerê vala ye</string>\n    <string name=\"compile_failed_with_info\">Optimîzasyon têk çû: </string>\n    <string name=\"sort_by_name\">Navê serîlêdanê</string>\n    <string name=\"sort_by_package_name\">Navê pakêtê</string>\n    <string name=\"sort_by_install_time\">Dema sazkirinê</string>\n    <string name=\"sort_by_update_time\">Wextê nûve bike</string>\n    <string name=\"sort_reverse\">Gara paşî</string>\n    <string name=\"menu_show_system_apps\">Sepanên pergalê</string>\n    <string name=\"menu_sort\">Rêzkirin</string>\n    <string name=\"enable_module\">Modulê çalak bike</string>\n    <string name=\"no_scope_selected\">Te tu sepanê hilnebijart. Berdewamkirin?</string>\n    <string name=\"menu_show_games\">Games</string>\n    <string name=\"menu_show_modules\">Modules</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Hilbijartina navnîşa çarçovê bi ser neket</string>\n    <string name=\"app_version\">Versiyon: %1$s</string>\n    <string name=\"use_recommended\">Pêşniyar kirin</string>\n    <string name=\"no_scope_selected_has_recommended\">Te tu sepanê hilnebijart. Serlêdanên pêşniyarkirî hilbijêrin?</string>\n    <string name=\"use_recommended_message\">Serlêdanên pêşniyarkirî hilbijêrin?</string>\n    <string name=\"module_is_not_activated_yet\">Modula Xposed hîn nehatiye çalak kirin</string>\n    <string name=\"requested_by_module\">Pêşniyar kirin</string>\n    <string name=\"update_available\">Nûvekirin heye: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modula %s ji ber ku tu sepan nehat hilbijartî hate neçalak kirin.</string>\n    <string name=\"android_framework\">Çarçoveya Sîstemê</string>\n    <string name=\"menu_backup_and_restore\">Backup</string>\n    <string name=\"menu_backup\">Backup</string>\n    <string name=\"menu_restore\">Nûvdekirin</string>\n    <string name=\"force_stop\">Bi zorê rawestandin</string>\n    <string name=\"force_stop_dlg_title\">Rawestandina zorê?</string>\n    <string name=\"force_stop_dlg_text\">Ger hûn bi zorê sepanek rawestînin, dibe ku ew xelet tevbigere.</string>\n    <string name=\"reboot_required\">Ji bo sepandina vê guherînê ji nû ve destpêkirinê hewce ye</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Veşartin</string>\n    <string name=\"deny_list\">%s di lîsteya redkirinê de ye. Dibe ku bandor neke.</string>\n    <string name=\"deny_list_info\">Li ser redkirina</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Di sepana din de bibînin</string>\n    <string name=\"module_app_info\">Agahdariya Appê</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nLi vir tiştek tune</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Çarçove</string>\n    <string name=\"settings_disable_verbose_log\">Têketinên devkî neçalak bike</string>\n    <string name=\"settings_disable_verbose_log_summary\">Daxwaza pirsgirêkan rapor bikin ku têketinên devkî tê de bin</string>\n    <string name=\"pure_black_dark_theme\">Mijara reş reş</string>\n    <string name=\"pure_black_dark_theme_summary\">Ger mijara tarî çalak be, mijara reş a paqij bikar bînin</string>\n    <string name=\"settings_group_theme\">Mijad</string>\n    <string name=\"settings_backup_and_restore\">Backup û restore</string>\n    <string name=\"settings_backup_summery\">Lîsteya modulê paşvekêşîn û navnîşên çarçovê.</string>\n    <string name=\"settings_restore_summery\">Lîsteya modul û navnîşên çarçovê vegerînin.</string>\n    <string name=\"settings_backup\">Backup</string>\n    <string name=\"settings_backup_failed2\">Piştgiriya bi ser neket:\\n%s</string>\n    <string name=\"enable_documentui\">Ji kerema xwe DocumentUI çalak bike</string>\n    <string name=\"settings_restore\">Nûvdekirin</string>\n    <string name=\"settings_restore_failed2\">Vegerandin bi ser neket:\\n%s</string>\n    <string name=\"group_network\">Network</string>\n    <string name=\"dns_over_http\">DNS li ser HTTPS</string>\n    <string name=\"dns_over_http_summary\">Li hin welatan jehrîkirina DNS-ê çareser bikin</string>\n    <string name=\"theme_color\">Rengê mijarê</string>\n    <string name=\"theme_color_system\">Rengê mijara pergalê</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Bi zorê serlêdanan bikin ku îkonên destpêker nîşan bidin</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Piştî Android 10, serîlêdan destûr nayê dayîn ku îkonên xwe yên destpêker veşêrin. Ji bo neçalakkirina vê taybetmendiya pergalê, guheztinê vekin.</string>\n    <string name=\"settings_group_system\">Sîstem</string>\n    <string name=\"settings_language\">Ziman</string>\n    <string name=\"settings_translation_contributors\">Beşdarên wergerê</string>\n    <string name=\"settings_translation\">Beşdarî wergerê bibin</string>\n    <string name=\"settings_translation_summary\">Alîkariya me bikin ku %s wergerînin zimanê we</string>\n    <string name=\"settings_create_shortcut_summary\">Kurtenivîsek çêbikin ku dikare rêveberê parazît veke</string>\n    <string name=\"settings_shortcut_pinned_hint\">Kurtenivîs pêçayî</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Destpêkera xwerû ya heyî kurtebirên pin piştgirî nake</string>\n    <string name=\"settings_enable_status_notification\">Notification Status</string>\n    <string name=\"settings_enable_status_notification_summary\">Agahdariyek nîşan bide ku dikare rêveberê parazît veke</string>\n    <string name=\"disable_status_notification_error\">Ne kurtebir, nekare ragihandinê neçalak bike</string>\n    <string name=\"settings_update_channel\">Kanalê nûve bikin</string>\n    <string name=\"update_channel_stable\">Stewr</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Avakirina şevê</string>\n    <string name=\"settings_xposed_api_call_protection\">Parastina banga Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Koda modulê ya bi dînamîk barkirî asteng bike ku Xposed API bikar bîne, ev dibe ku hin modulan bişkîne lê ji ewlehiyê sûd werdigire</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Releases</string>\n    <string name=\"module_information\">Info</string>\n    <string name=\"module_information_homepage\">Homepage</string>\n    <string name=\"module_information_source_url\">Koda çavkaniyê</string>\n    <string name=\"module_information_collaborators\">Hevkar</string>\n    <string name=\"module_release_view_assets\">Tiştan</string>\n    <string name=\"menu_open_in_browser\">Di gerokê de vekin</string>\n    <string name=\"module_release_load_more\">Guhertoyên kevntir nîşan bide</string>\n    <string name=\"module_release_no_more\">Bêtir berdan</string>\n    <string name=\"repo_load_failed\">Barkirina depoya modulê têk çû: %s</string>\n    <string name=\"sort_upgradable_first\">Pêşî nûvekirin</string>\n    <string name=\"installed\">Saz kirin</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d download</item>\n        <item quantity=\"other\">%d downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">sor</string>\n    <string name=\"color_pink\">Pembe</string>\n    <string name=\"color_purple\">Mor</string>\n    <string name=\"color_deep_purple\">binefşî ya kûr</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Şîn</string>\n    <string name=\"color_light_blue\">Şînê vebûyî</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">Kesk</string>\n    <string name=\"color_light_green\">Kesk ronî</string>\n    <string name=\"color_lime\">Lime</string>\n    <string name=\"color_yellow\">Zer</string>\n    <string name=\"color_amber\">Aqût</string>\n    <string name=\"color_orange\">porteqalî</string>\n    <string name=\"color_deep_orange\">porteqala kûr</string>\n    <string name=\"color_brown\">qehweyî</string>\n    <string name=\"color_blue_grey\">Şîn gewr</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-lt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Apžvalga</string>\n    <string name=\"Modules\">Moduliai</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modulis įjungtas</item>\n        <item quantity=\"few\">%d įjungti moduliai</item>\n        <item quantity=\"many\">%d įjungti moduliai</item>\n        <item quantity=\"other\">%d įjungti moduliai</item>\n    </plurals>\n    <string name=\"Logs\">Žurnalai</string>\n    <string name=\"Settings\">Nustatymai</string>\n    <string name=\"feedback_or_suggestion\">Atsiliepimai arba pasiūlymas</string>\n    <string name=\"About\">About-face</string>\n    <string name=\"report_issue\">Pranešti apie problemą</string>\n    <string name=\"module_repo\">Saugykla</string>\n    <string name=\"module_repo_up_to_date\">Visi moduliai atnaujinti</string>\n    <string name=\"module_repo_published_time\">Paskelbta adresu %s</string>\n    <string name=\"module_repo_updated_time\">Atnaujinta %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modulis atnaujinamas</item>\n        <item quantity=\"few\">%d atnaujinami moduliai</item>\n        <item quantity=\"many\">%d atnaujinami moduliai</item>\n        <item quantity=\"other\">%d atnaujinami moduliai</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Peržiūrėkite šaltinio kodą adresu %1$s<br/>Prisijunkite prie mūsų %2$s kanalo]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Ace Miller, im sorry</string>\n    <string name=\"install\">Įdiekite</string>\n    <string name=\"install_summary\">Bakstelėkite, jei norite įdiegti LSPosed</string>\n    <string name=\"not_installed\">Neįdiegta</string>\n    <string name=\"not_install_summary\">\\\"LSPosed\\\" nėra įdiegta</string>\n    <string name=\"activated\">Aktyvuota</string>\n    <string name=\"partial_activated\">Iš dalies aktyvuota</string>\n    <string name=\"selinux_policy_not_loaded_summary\">\\\"SEPolicy\\\" nėra tinkamai įkelta</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Kai kurios funkcijos neveiks.<br/>Apie tai praneškite <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> kūrėjui.]]></string>\n    <string name=\"system_inject_fail_summary\">Nepavyko įšvirkšti sistemos pagrindo</string>\n    <string name=\"system_inject_fail\"><![CDATA[Tai pasitaiko retai ir gali būti dėl <b>\"Magisk\"</b> arba kai kurių nekokybiškų \"Magisk\" modulių.<br/>Pabandykite išjungti kitus \"Magisk\" modulius, išskyrus \"Riru\" ir \"LSPosed\", arba pateikite visą žurnalą kūrėjams.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Neteisingas sistemos rekvizitas</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Ištrintos arba pakeistos kai kurios būtinos sistemos savybės.<br/>Moduliai kartais gali būti pripažinti negaliojančiais.]]></string>\n    <string name=\"need_update\">Reikia atnaujinti</string>\n    <string name=\"please_update_summary\">Įdiekite naujausią \\\"LSPosed\\\" versiją</string>\n    <string name=\"info_api_version\">API versija</string>\n    <string name=\"info_framework_version\">Pagrindų versija</string>\n    <string name=\"info_manager_package_name\">Valdytojo paketo pavadinimas</string>\n    <string name=\"info_system_version\">Sistemos versija</string>\n    <string name=\"info_device\">Įrenginys</string>\n    <string name=\"info_system_abi\">Sistemos ABI</string>\n    <string name=\"info_dex2oat_wrapper\">\\\"Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Įjungta</string>\n    <string name=\"not_enabled\">Neįjungta</string>\n    <string name=\"supported\">Palaikomas</string>\n    <string name=\"unsupported\">Nepalaikomas</string>\n    <string name=\"android_version_unsatisfied\">\\\"Android\\\" versija nepatenkinti</string>\n    <string name=\"crashed\">Sugedo</string>\n    <string name=\"mount_failed\">Montavimas nepavyko</string>\n    <string name=\"selinux_permissive\">\\\"SELinux\\\" yra leidžiamoji</string>\n    <string name=\"sepolicy_incorrect\">\\\"SELinux\\\" politika yra neteisinga</string>\n    <string name=\"update_lsposed\">Atnaujinti LSPosed</string>\n    <string name=\"update_lsposed_msg\">Patvirtinkite, kad atnaujintumėte LSPosed? Baigus atnaujinimą šis prietaisas bus perkrautas</string>\n    <string name=\"info_copied\">Nukopijuota į iškarpinę</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Sveiki atvykę į LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Jūs naudojate parazitinį tvarkytuvą, kuris gali sukurti nuorodą arba vis dar atidaryti iš pranešimo.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Naudojate parazitinį tvarkytuvą, kuris gali būti atidarytas iš pranešimo.</string>\n    <string name=\"create_shortcut\">Sukurti nuorodą</string>\n    <string name=\"never_show\">Niekada nerodykite</string>\n    <string name=\"app_welcome\">Rekomenduojama parazitinė vadybininkė</string>\n    <string name=\"app_welcome_summary\">\\\"LSPosed\\\" dabar palaiko sistemos parazitavimą, kad išvengtų aptikimo, galite atidaryti parazitų tvarkyklę iš pranešimo. Rekomenduojama pašalinti dabartinę programą.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Išsaugoti</string>\n    <string name=\"nav_item_logs_verbose\">Žurnalai su verbaline informacija</string>\n    <string name=\"nav_item_logs_module\">Modulių žurnalai</string>\n    <string name=\"logs_saving\">Įrašomas žurnalas, palaukite</string>\n    <string name=\"logs_saved\">Išsaugoti žurnalai</string>\n    <string name=\"logs_save_failed2\">Nepavyko išsaugoti:\\n%s</string>\n    <string name=\"menuClearLog\">Išvalyti žurnalą dabar</string>\n    <string name=\"logs_cleared\">Žurnalas sėkmingai išvalytas.</string>\n    <string name=\"scroll_top\">Slinkti į viršų</string>\n    <string name=\"loading\">Įkrovimas…</string>\n    <string name=\"scroll_bottom\">Slinkite į apačią</string>\n    <string name=\"menuReload\">Perkrauti</string>\n    <string name=\"logs_clear_failed_2\">Nepavyko išvalyti žurnalo</string>\n    <string name=\"menu_enable_word_wrap\">Žodžių apvyniojimas</string>\n    <string name=\"enabled_verbose_log\">Įjungtas verstinis žurnalas</string>\n    <string name=\"disabled_verbose_log\">Išjungtas verstinis žurnalas</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(aprašymas nepateiktas)</string>\n    <string name=\"warning_xposed_min_version\">Šis modulis reikalauja naujesnės \\\"Xposed\\\" versijos (%d), todėl negali būti aktyvuotas</string>\n    <string name=\"warning_target_version_higher\">Šis modulis skirtas naujesnei \\\"Xposed\\\" versijai (%d), todėl kai kurios funkcijos gali neveikti.</string>\n    <string name=\"no_min_version_specified\">Šis modulis nenurodo jam reikalingos \\\"Xposed\\\" versijos.</string>\n    <string name=\"warning_min_version_too_low\">Šis modulis buvo sukurtas \\\"Xposed\\\" versijai %1$d, tačiau dėl nesuderinamų pakeitimų versijoje %2$d, jis buvo išjungtas.</string>\n    <string name=\"warning_installed_on_external_storage\">Šio modulio negalima įkelti, nes jis įdiegtas SD kortelėje, perkelkite jį į vidinę saugyklą</string>\n    <string name=\"module_uninstall\">Pašalinti</string>\n    <string name=\"module_settings\">Modulio nustatymai</string>\n    <string name=\"view_in_repo\">Peržiūrėti Repo</string>\n    <string name=\"module_uninstall_message\">Ar norite pašalinti šį modulį?</string>\n    <string name=\"module_uninstalled\">Išmontuota %1$s</string>\n    <string name=\"module_uninstall_failed\">Nesėkmingas pašalinimas</string>\n    <string name=\"add_module_to_user\">Pridėti modulį prie naudotojo</string>\n    <string name=\"module_installed\">Pridėta %1$s naudotojui %2$s</string>\n    <string name=\"module_install_failed\">Nepavyko pridėti modulio</string>\n    <string name=\"install_to_user\">Įdiegti naudotojui %s</string>\n    <string name=\"install_to_user_message\">Norite įdiegti %1$s naudotojui %2$s? Rekomenduojama įdiegti rankiniu būdu, priverstinis diegimas per LSPosed gali sukelti problemų.</string>\n    <string name=\"expand\">išplėsti</string>\n    <string name=\"collapse\">žlugimas</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Iš naujo optimizuoti</string>\n    <string name=\"compile_speed_msg\">Optimizavimas…</string>\n    <string name=\"compile_done\">Optimizavimas baigtas</string>\n    <string name=\"app_launch\">Paleiskite jį</string>\n    <string name=\"compile_failed\">Optimizavimas nepavyko: grąžinama vertė yra tuščia</string>\n    <string name=\"compile_failed_with_info\">Optimizavimas nepavyko: </string>\n    <string name=\"sort_by_name\">Programos pavadinimas</string>\n    <string name=\"sort_by_package_name\">Paketo pavadinimas</string>\n    <string name=\"sort_by_install_time\">Diegimo laikas</string>\n    <string name=\"sort_by_update_time\">Atnaujinimo laikas</string>\n    <string name=\"sort_reverse\">Atvirkštinis</string>\n    <string name=\"menu_show_system_apps\">Sistemos programos</string>\n    <string name=\"menu_sort\">Rūšiavimas</string>\n    <string name=\"enable_module\">Įjungti modulį</string>\n    <string name=\"no_scope_selected\">Nepasirinkote jokios programos. Tęsti?</string>\n    <string name=\"menu_show_games\">Žaidimai</string>\n    <string name=\"menu_show_modules\">Moduliai</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Nepavyko išsaugoti srities sąrašo</string>\n    <string name=\"app_version\">Versija: %1$s</string>\n    <string name=\"use_recommended\">Rekomenduojama</string>\n    <string name=\"no_scope_selected_has_recommended\">Nepasirinkote jokios programos. Pasirinkti rekomenduojamas programas?</string>\n    <string name=\"use_recommended_message\">Pasirinkite rekomenduojamas programas?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modulis dar nėra aktyvuotas</string>\n    <string name=\"requested_by_module\">Rekomenduojama</string>\n    <string name=\"update_available\">Galimas atnaujinimas: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modulis %s buvo išjungtas, nes nebuvo pasirinkta jokia programa.</string>\n    <string name=\"android_framework\">Sistemos sistema</string>\n    <string name=\"menu_backup_and_restore\">Atsarginė kopija</string>\n    <string name=\"menu_backup\">Atsarginė kopija</string>\n    <string name=\"menu_restore\">Atkurti</string>\n    <string name=\"force_stop\">Priverstinis sustabdymas</string>\n    <string name=\"force_stop_dlg_title\">Priverstinis sustojimas?</string>\n    <string name=\"force_stop_dlg_text\">Jei priverstinai sustabdysite programą, ji gali elgtis netinkamai.</string>\n    <string name=\"reboot_required\">Kad šis pakeitimas būtų pritaikytas, reikia perkrauti kompiuterį</string>\n    <string name=\"reboot\">Perkraukite</string>\n    <string name=\"menu_hide\">Paslėpti</string>\n    <string name=\"deny_list\">%s yra denylist. Jis gali neįsigalioti.</string>\n    <string name=\"deny_list_info\">Dėl denylist</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Peržiūrėti kitoje programoje</string>\n    <string name=\"module_app_info\">Programėlės informacija</string>\n    <string name=\"list_empty\">\\\\\\\\_(ツ)_\\/¯\\nČia nieko nėra</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Sistema</string>\n    <string name=\"settings_disable_verbose_log\">Išjungti verbalinius žurnalus</string>\n    <string name=\"settings_disable_verbose_log_summary\">Ataskaitos klausimų prašymas įtraukti verbalinius žurnalus</string>\n    <string name=\"pure_black_dark_theme\">Juoda tamsi tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Naudokite grynai juodą temą, jei įjungta tamsi tema</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Atsarginės kopijos kūrimas ir atkūrimas</string>\n    <string name=\"settings_backup_summery\">Atsarginės kopijos modulių ir sričių sąrašai.</string>\n    <string name=\"settings_restore_summery\">Atkurti modulių ir sričių sąrašus.</string>\n    <string name=\"settings_backup\">Atsarginė kopija</string>\n    <string name=\"settings_backup_failed2\">Nepavyko sukurti atsarginės kopijos:\\n%s</string>\n    <string name=\"enable_documentui\">Įjunkite DocumentUI</string>\n    <string name=\"settings_restore\">Atkurti</string>\n    <string name=\"settings_restore_failed2\">Nepavyko atkurti:\\n%s</string>\n    <string name=\"group_network\">Tinklas</string>\n    <string name=\"dns_over_http\">DNS per HTTPS</string>\n    <string name=\"dns_over_http_summary\">Apėjimas DNS apsinuodijimas kai kuriose šalyse</string>\n    <string name=\"theme_color\">Temos spalva</string>\n    <string name=\"theme_color_system\">Sistemos temos spalva</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Priversti programas rodyti paleidiklio piktogramas</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Po \\\"Android 10\\\" programėlėms neleidžiama slėpti paleidimo programos piktogramų. Norėdami išjungti šią sistemos funkciją, išjunkite perjungiklį.</string>\n    <string name=\"settings_group_system\">Sistema</string>\n    <string name=\"settings_language\">Kalba</string>\n    <string name=\"settings_translation_contributors\">Vertimo paslaugų teikėjai</string>\n    <string name=\"settings_translation\">Dalyvaukite vertimo procese</string>\n    <string name=\"settings_translation_summary\">Padėkite mums išversti %s į savo kalbą</string>\n    <string name=\"settings_create_shortcut_summary\">Sukurti nuorodą, kuri gali atidaryti parazitinį tvarkytuvą</string>\n    <string name=\"settings_shortcut_pinned_hint\">Prisegtas trumpinys</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Dabartinė numatytoji paleidimo programa nepalaiko prisegamų nuorodų</string>\n    <string name=\"settings_enable_status_notification\">Pranešimas apie būseną</string>\n    <string name=\"settings_enable_status_notification_summary\">Rodyti pranešimą, kad galima atidaryti parazitinį tvarkytuvą</string>\n    <string name=\"disable_status_notification_error\">Nėra sparčiųjų klavišų, negalima išjungti pranešimo</string>\n    <string name=\"settings_update_channel\">Atnaujinti kanalą</string>\n    <string name=\"update_channel_stable\">Stabilus</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Naktinis kūrimas</string>\n    <string name=\"settings_xposed_api_call_protection\">\\\"Xposed API\\\" skambučių apsauga</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokuoti dinamiškai įkeltą modulio kodą, kad būtų galima naudoti Xposed API, tai gali pažeisti kai kuriuos modulius, bet naudinga saugumui</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Leidiniai</string>\n    <string name=\"module_information\">Informacija</string>\n    <string name=\"module_information_homepage\">Pradžia</string>\n    <string name=\"module_information_source_url\">Šaltinio kodas</string>\n    <string name=\"module_information_collaborators\">Bendradarbiai</string>\n    <string name=\"module_release_view_assets\">Turtas</string>\n    <string name=\"menu_open_in_browser\">Atidaryti naršyklėje</string>\n    <string name=\"module_release_load_more\">Rodyti senesnes versijas</string>\n    <string name=\"module_release_no_more\">Jokio išleidimo</string>\n    <string name=\"repo_load_failed\">Nepavyko įkelti modulio atramos: %s</string>\n    <string name=\"sort_upgradable_first\">Pirmiausia atnaujinamas</string>\n    <string name=\"installed\">Įdiegta</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d atsisiųsti</item>\n        <item quantity=\"few\">%d Parsisiųsti</item>\n        <item quantity=\"many\">%d Parsisiųsti</item>\n        <item quantity=\"other\">%d Parsisiųsti</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Raudona</string>\n    <string name=\"color_pink\">Rožinis</string>\n    <string name=\"color_purple\">Violetinė</string>\n    <string name=\"color_deep_purple\">Tamsiai violetinė</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Mėlyna</string>\n    <string name=\"color_light_blue\">Šviesiai mėlyna</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">Žalioji</string>\n    <string name=\"color_light_green\">Šviesiai žalia</string>\n    <string name=\"color_lime\">Lime</string>\n    <string name=\"color_yellow\">Geltona</string>\n    <string name=\"color_amber\">Amber</string>\n    <string name=\"color_orange\">Oranžinė</string>\n    <string name=\"color_deep_orange\">Tamsiai oranžinė</string>\n    <string name=\"color_brown\">Ruda</string>\n    <string name=\"color_blue_grey\">Mėlyna pilka</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2023 LSPosed Contributors\n  -->\n\n<resources>\n    <color name=\"primary_text_material_inverse\">@color/abc_primary_text_material_light</color>\n    <color name=\"primary_text_material\">@color/abc_primary_text_material_dark</color>\n\n    <color name=\"ic_launcher_foreground\">#F06292</color>\n    <color name=\"ic_launcher_background\">#E1F5FE</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/styles.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n\n    <style name=\"AppTheme\" parent=\"Theme\" />\n\n    <style name=\"ThemeOverlay.MaterialRed\" parent=\"ThemeOverlay.Dark.MaterialRed\"/>\n    <style name=\"ThemeOverlay.MaterialPink\" parent=\"ThemeOverlay.Dark.MaterialPink\"/>\n    <style name=\"ThemeOverlay.MaterialPurple\" parent=\"ThemeOverlay.Dark.MaterialPurple\"/>\n    <style name=\"ThemeOverlay.MaterialDeepPurple\" parent=\"ThemeOverlay.Dark.MaterialDeepPurple\"/>\n    <style name=\"ThemeOverlay.MaterialIndigo\" parent=\"ThemeOverlay.Dark.MaterialIndigo\"/>\n    <style name=\"ThemeOverlay.MaterialBlue\" parent=\"ThemeOverlay.Dark.MaterialBlue\"/>\n    <style name=\"ThemeOverlay.MaterialLightBlue\" parent=\"ThemeOverlay.Dark.MaterialLightBlue\"/>\n    <style name=\"ThemeOverlay.MaterialCyan\" parent=\"ThemeOverlay.Dark.MaterialCyan\"/>\n    <style name=\"ThemeOverlay.MaterialTeal\" parent=\"ThemeOverlay.Dark.MaterialTeal\"/>\n    <style name=\"ThemeOverlay.MaterialGreen\" parent=\"ThemeOverlay.Dark.MaterialGreen\"/>\n    <style name=\"ThemeOverlay.MaterialLightGreen\" parent=\"ThemeOverlay.Dark.MaterialLightGreen\"/>\n    <style name=\"ThemeOverlay.MaterialLime\" parent=\"ThemeOverlay.Dark.MaterialLime\"/>\n    <style name=\"ThemeOverlay.MaterialYellow\" parent=\"ThemeOverlay.Dark.MaterialYellow\"/>\n    <style name=\"ThemeOverlay.MaterialAmber\" parent=\"ThemeOverlay.Dark.MaterialAmber\"/>\n    <style name=\"ThemeOverlay.MaterialOrange\" parent=\"ThemeOverlay.Dark.MaterialOrange\"/>\n    <style name=\"ThemeOverlay.MaterialDeepOrange\" parent=\"ThemeOverlay.Dark.MaterialDeepOrange\"/>\n    <style name=\"ThemeOverlay.MaterialBrown\" parent=\"ThemeOverlay.Dark.MaterialBrown\"/>\n    <style name=\"ThemeOverlay.MaterialBlueGrey\" parent=\"ThemeOverlay.Dark.MaterialBlueGrey\"/>\n    <style name=\"ThemeOverlay.MaterialSakura\" parent=\"ThemeOverlay.Dark.MaterialSakura\"/>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night-v31/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2023 LSPosed Contributors\n  -->\n\n<resources>\n    <color name=\"ic_launcher_foreground\">@android:color/system_accent1_800</color>\n    <color name=\"ic_launcher_background\">@android:color/system_accent1_200</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Overzicht</string>\n    <string name=\"Modules\">Modules</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d module ingeschakeld</item>\n        <item quantity=\"other\">%d modules ingeschakeld</item>\n    </plurals>\n    <string name=\"Logs\">Logboeken</string>\n    <string name=\"Settings\">Instellingen</string>\n    <string name=\"feedback_or_suggestion\">Feedback en suggestie</string>\n    <string name=\"About\">Over</string>\n    <string name=\"report_issue\">Probleem melden</string>\n    <string name=\"module_repo\">Opslagplaats</string>\n    <string name=\"module_repo_up_to_date\">Alle modules zijn up-to-date</string>\n    <string name=\"module_repo_published_time\">Gepubliceerd op %s</string>\n    <string name=\"module_repo_updated_time\">Bijgewerkt op %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d module te upgraden</item>\n        <item quantity=\"other\">%d modules te upgraden</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Bekijk de broncode op %1$s<br/>Sluit je aan bij onze %2$s kanaal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Pandemic-XDA</string>\n    <string name=\"install\">Installeren</string>\n    <string name=\"install_summary\">Tik om LSPosed te installeren</string>\n    <string name=\"not_installed\">Niet geïnstalleerd</string>\n    <string name=\"not_install_summary\">LSPosed is niet geinstalleerd</string>\n    <string name=\"activated\">Geactiveerd</string>\n    <string name=\"partial_activated\">Gedeeltelijk geactiveerd</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy is niet correct geladen</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Sommige functies zullen niet werken.<br/>Gelieve dit te melden aan <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> ontwikkelaar.]]></string>\n    <string name=\"system_inject_fail_summary\">Systeem Framework injectie is mislukt</string>\n    <string name=\"system_inject_fail\"><![CDATA[Systeem Framework injectie mislukt.<br/>Dit is zeldzaam en kan worden veroorzaakt door <b>Magisk</b> of sommige Magisk-modules van lage kwaliteit.<br/>Probeer andere Magisk-modules dan Riru en LSPosed uit te schakelen of stuur een volledig logboek naar de ontwikkelaars.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Systeem prop incorrect</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Enkele noodzakelijke systeemeigenschappen verwijderd of gewijzigd.<br/>Modules kunnen af en toe ongeldig worden.]]></string>\n    <string name=\"need_update\">Bijwerken is nodig</string>\n    <string name=\"please_update_summary\">Installeer de laatste versie van LSPosed</string>\n    <string name=\"info_api_version\">API versie</string>\n    <string name=\"info_framework_version\">Framework versie</string>\n    <string name=\"info_manager_package_name\">Naam beheerderspakket</string>\n    <string name=\"info_system_version\">Systeem versie</string>\n    <string name=\"info_device\">Apparaat</string>\n    <string name=\"info_system_abi\">Systeem ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Ingeschakeld</string>\n    <string name=\"not_enabled\">Niet ingeschakeld</string>\n    <string name=\"supported\">Ondersteund</string>\n    <string name=\"unsupported\">Niet-ondersteunde</string>\n    <string name=\"android_version_unsatisfied\">Android versie onbevredigd</string>\n    <string name=\"crashed\">Crashed</string>\n    <string name=\"mount_failed\">Berg mislukt</string>\n    <string name=\"selinux_permissive\">SELinux is permissief</string>\n    <string name=\"sepolicy_incorrect\">SELinux beleid is onjuist</string>\n    <string name=\"update_lsposed\">Update LSPosed</string>\n    <string name=\"update_lsposed_msg\">Bevestig bijwerken van LSPosed? Dit apparaat zal opnieuw opstarten nadat de update is voltooid</string>\n    <string name=\"info_copied\">Gekopieerd naar klembord</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Welkom bij LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">U gebruikt de parasitaire manager, die een snelkoppeling kan maken of nog steeds kan openen vanuit een melding.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Je gebruikt de parasiterende manager, die kan openen vanuit de melding.</string>\n    <string name=\"create_shortcut\">Snelkoppeling maken</string>\n    <string name=\"never_show\">Nooit tonen</string>\n    <string name=\"app_welcome\">Parasitaire manager aanbevolen</string>\n    <string name=\"app_welcome_summary\">LSPosed ondersteunt nu systeemparasitisatie om detectie te voorkomen, u kunt parasitaire manager openen vanuit een melding. Het wordt aanbevolen om de huidige applicatie te verwijderen.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Opslaan</string>\n    <string name=\"nav_item_logs_verbose\">Uitgebreide logs</string>\n    <string name=\"nav_item_logs_module\">Modules Logbestanden</string>\n    <string name=\"logs_saving\">Logboek wordt opgeslagen, even geduld a.u.b.</string>\n    <string name=\"logs_saved\">Opgeslagen logboeken</string>\n    <string name=\"logs_save_failed2\">Opslaan mislukt:\\n%s</string>\n    <string name=\"menuClearLog\">Wis het logboek nu</string>\n    <string name=\"logs_cleared\">Log succesvol gewist.</string>\n    <string name=\"scroll_top\">Scroll naar boven</string>\n    <string name=\"loading\">Laden…</string>\n    <string name=\"scroll_bottom\">Scroll naar beneden</string>\n    <string name=\"menuReload\">Herlaad</string>\n    <string name=\"logs_clear_failed_2\">Het logboek kan niet worden gewist</string>\n    <string name=\"menu_enable_word_wrap\">Woord Omslag</string>\n    <string name=\"enabled_verbose_log\">Uitgebreide logboek ingeschakeld</string>\n    <string name=\"disabled_verbose_log\">Verbose logboek uitgeschakeld</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(geen beschrijving gegeven)</string>\n    <string name=\"warning_xposed_min_version\">Deze module vereist een nieuwere versie van Lsposed (%d) en kan dus niet worden geactiveerd</string>\n    <string name=\"warning_target_version_higher\">Deze module is ontworpen voor een nieuwere Xposed-versie (%d) en daarom werken sommige functionaliteiten mogelijk niet</string>\n    <string name=\"no_min_version_specified\">Deze module heeft geen vereiste versie voor Xposed opgegeven.</string>\n    <string name=\"warning_min_version_too_low\">Deze module is gemaakt voor de Lsposed-versie %1$d, maar vanwege onverenigbare wijzigingen in de versie %2$d, is het uitgeschakeld</string>\n    <string name=\"warning_installed_on_external_storage\">Deze module kan niet worden geladen omdat deze op de SD-kaart is geïnstalleerd. Verplaats deze naar de interne opslag</string>\n    <string name=\"module_uninstall\">Verwijderen</string>\n    <string name=\"module_settings\">Module instellingen</string>\n    <string name=\"view_in_repo\">Bekijk in Repo</string>\n    <string name=\"module_uninstall_message\">Wilt u deze module verwijderen?</string>\n    <string name=\"module_uninstalled\">Gedeïnstalleerd %1$s</string>\n    <string name=\"module_uninstall_failed\">Verwijderen mislukt</string>\n    <string name=\"add_module_to_user\">Module toevoegen aan gebruiker</string>\n    <string name=\"module_installed\">Toegevoegd %1$s naar gebruiker %2$s</string>\n    <string name=\"module_install_failed\">Het toevoegen van de module is mislukt</string>\n    <string name=\"install_to_user\">Installeer naar gebruiker %s</string>\n    <string name=\"install_to_user_message\">Wil je installeren %1$s naar gebruiker %2$s? Het wordt aanbevolen om handmatig te installeren, het forceren van installatie via LSPosed kan problemen veroorzaken.</string>\n    <string name=\"expand\">uitbreiden</string>\n    <string name=\"collapse\">samenvouwen</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Opnieuw optimaliseren</string>\n    <string name=\"compile_speed_msg\">Optimaliseren…</string>\n    <string name=\"compile_done\">Optimalisatie voltooid.</string>\n    <string name=\"app_launch\">Lanceer het</string>\n    <string name=\"compile_failed\">Optimalisatie mislukt of geretourneerde waarde is leeg.</string>\n    <string name=\"compile_failed_with_info\">Optimalisatie is mislukt: </string>\n    <string name=\"sort_by_name\">Sorteer op applicatienaam</string>\n    <string name=\"sort_by_package_name\">Sorteer op pakketnaam</string>\n    <string name=\"sort_by_install_time\">Sorteer op installatietijd</string>\n    <string name=\"sort_by_update_time\">Sorteer op updatetijd</string>\n    <string name=\"sort_reverse\">Omkeren</string>\n    <string name=\"menu_show_system_apps\">Systeem apps</string>\n    <string name=\"menu_sort\">Sorteren</string>\n    <string name=\"enable_module\">Schakel module in</string>\n    <string name=\"no_scope_selected\">U heeft geen app geselecteerd. Doorgaan?</string>\n    <string name=\"menu_show_games\">Games</string>\n    <string name=\"menu_show_modules\">Modules</string>\n    <string name=\"menu_show_denylist\">Weigerlijst</string>\n    <string name=\"failed_to_save_scope_list\">Scopelijst opslaan is mislukt</string>\n    <string name=\"app_version\">Versie: %1$s</string>\n    <string name=\"use_recommended\">Aanbevolen</string>\n    <string name=\"no_scope_selected_has_recommended\">U heeft geen app geselecteerd. Aanbevolen apps selecteren?</string>\n    <string name=\"use_recommended_message\">Aanbevolen apps selecteren?</string>\n    <string name=\"module_is_not_activated_yet\">LSPosed module is nog niet geactiveerd</string>\n    <string name=\"requested_by_module\">Aanbevolen</string>\n    <string name=\"update_available\">Update beschikbaar: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Module %1$s is uitgeschakeld omdat er geen app is geselecteerd.</string>\n    <string name=\"android_framework\">Systeem Framework</string>\n    <string name=\"menu_backup_and_restore\">Backup</string>\n    <string name=\"menu_backup\">Backup</string>\n    <string name=\"menu_restore\">Herstellen</string>\n    <string name=\"force_stop\">Forceer stop</string>\n    <string name=\"force_stop_dlg_title\">Gedwongen stoppen?</string>\n    <string name=\"force_stop_dlg_text\">Als je een app gedwongen stopt, kan deze onverwacht gedrag vertonen.</string>\n    <string name=\"reboot_required\">Opnieuw opstarten is vereist om deze wijziging door te voeren</string>\n    <string name=\"reboot\">Herstart</string>\n    <string name=\"menu_hide\">Verbergen</string>\n    <string name=\"deny_list\">%s staat op de weigerlijst en kan geen effect hebben.</string>\n    <string name=\"deny_list_info\">Op weigerlijst</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Bekijk in andere app</string>\n    <string name=\"module_app_info\">App informatie</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nNiets hier</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Schakel uitgebreide logboeken uit</string>\n    <string name=\"settings_disable_verbose_log_summary\">Rapporteer problemen verzoek om uitgebreide logboeken op te nemen</string>\n    <string name=\"pure_black_dark_theme\">Zwarte donkere thema</string>\n    <string name=\"pure_black_dark_theme_summary\">Gebruik het pure zwarte thema als het donkere thema is ingeschakeld</string>\n    <string name=\"settings_group_theme\">Thema</string>\n    <string name=\"settings_backup_and_restore\">Backup en herstellen</string>\n    <string name=\"settings_backup_summery\">Backup of herstel de modulelijst en scooplijsten.</string>\n    <string name=\"settings_restore_summery\">Herstel de modulelijst en scooplijsten.</string>\n    <string name=\"settings_backup\">Backup</string>\n    <string name=\"settings_backup_failed2\">Opslaan mislukt:\\n%s</string>\n    <string name=\"enable_documentui\">Schakel DocumentUI in</string>\n    <string name=\"settings_restore\">Herstellen</string>\n    <string name=\"settings_restore_failed2\">Herstellen mislukt:\\n%s</string>\n    <string name=\"group_network\">Netwerk</string>\n    <string name=\"dns_over_http\">DNS via HTTPS</string>\n    <string name=\"dns_over_http_summary\">Tijdelijke oplossing voor DNS-vergiftiging in sommige landen</string>\n    <string name=\"theme_color\">Thema kleur</string>\n    <string name=\"theme_color_system\">Systeem thema kleur</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Forceer apps om launcher-pictogrammen weer te geven</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Na Android 10 mogen apps de launcher pictogrammen niet verbergen. Schakel de schakelaar uit om deze functie uit te schakelen.</string>\n    <string name=\"settings_group_system\">Systeem</string>\n    <string name=\"settings_language\">Taal</string>\n    <string name=\"settings_translation_contributors\">Bijdragers van vertalingen</string>\n    <string name=\"settings_translation\">Deelnemen aan vertaling</string>\n    <string name=\"settings_translation_summary\">Help ons %s te vertalen in uw taal</string>\n    <string name=\"settings_create_shortcut_summary\">Maak een snelkoppeling die parasitaire manager kan openen</string>\n    <string name=\"settings_shortcut_pinned_hint\">Snelkoppeling vastgezet</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">De huidige standaard launcher ondersteunt geen pin-snelkoppelingen</string>\n    <string name=\"settings_enable_status_notification\">Statusmelding</string>\n    <string name=\"settings_enable_status_notification_summary\">Toon een melding die parasitaire manager kan openen</string>\n    <string name=\"disable_status_notification_error\">Geen snelkoppeling, kan melding niet uitschakelen</string>\n    <string name=\"settings_update_channel\">Kanaal bijwerken</string>\n    <string name=\"update_channel_stable\">Stabiel</string>\n    <string name=\"update_channel_bate\">Bèta</string>\n    <string name=\"update_channel_nightly\">Nachtelijke bouw</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API-oproepbeveiliging</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokkeer dynamisch geladen modulecode om Xposed API te gebruiken, dit kan sommige modules breken maar komt de veiligheid ten goede</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Leesmij</string>\n    <string name=\"module_releases\">Publicaties</string>\n    <string name=\"module_information\">Informatie</string>\n    <string name=\"module_information_homepage\">Startpagina</string>\n    <string name=\"module_information_source_url\">Broncode</string>\n    <string name=\"module_information_collaborators\">Medewerkers</string>\n    <string name=\"module_release_view_assets\">Middelen</string>\n    <string name=\"menu_open_in_browser\">Open in de browser</string>\n    <string name=\"module_release_load_more\">Laat oudere versies zien</string>\n    <string name=\"module_release_no_more\">Geen release meer</string>\n    <string name=\"repo_load_failed\">Kan moduleopslagplaats niet laden: %1$s</string>\n    <string name=\"sort_upgradable_first\">Eerst te upgraden</string>\n    <string name=\"installed\">Geïnstalleerd</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d download</item>\n        <item quantity=\"other\">%d downloads</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rood</string>\n    <string name=\"color_pink\">Roze</string>\n    <string name=\"color_purple\">Paars</string>\n    <string name=\"color_deep_purple\">Donker paars</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blauw</string>\n    <string name=\"color_light_blue\">Licht blauw</string>\n    <string name=\"color_cyan\">Turquoise</string>\n    <string name=\"color_teal\">Groenblauw</string>\n    <string name=\"color_green\">Groen</string>\n    <string name=\"color_light_green\">Licht groen</string>\n    <string name=\"color_lime\">Limoen</string>\n    <string name=\"color_yellow\">Geel</string>\n    <string name=\"color_amber\">Amber</string>\n    <string name=\"color_orange\">Oranje</string>\n    <string name=\"color_deep_orange\">Diep oranje</string>\n    <string name=\"color_brown\">Bruin</string>\n    <string name=\"color_blue_grey\">Blauw grijs</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-no/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Oversikt</string>\n    <string name=\"Modules\">Moduler</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul aktivert</item>\n        <item quantity=\"other\">%d moduler aktivert</item>\n    </plurals>\n    <string name=\"Logs\">Logger</string>\n    <string name=\"Settings\">Innstillinger</string>\n    <string name=\"feedback_or_suggestion\">Tilbakemelding eller forslag</string>\n    <string name=\"About\">Om</string>\n    <string name=\"report_issue\">Rapporter en feil</string>\n    <string name=\"module_repo\">Kodelager</string>\n    <string name=\"module_repo_up_to_date\">Alle moduler oppdatert</string>\n    <string name=\"module_repo_published_time\">Publisert på %s</string>\n    <string name=\"module_repo_updated_time\">Oppdatert kl %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul kan oppgraderes</item>\n        <item quantity=\"other\">%d moduler kan oppgraderes</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Vis kildekode på %1$s<br/>Bli med i vår %2$s -kanal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Installer</string>\n    <string name=\"install_summary\">Trykk for å installere LSPosed</string>\n    <string name=\"not_installed\">Ikke installert</string>\n    <string name=\"not_install_summary\">LSPC er ikke installert</string>\n    <string name=\"activated\">Aktivert</string>\n    <string name=\"partial_activated\">Delvis aktivert</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy er ikke lastet riktig</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Noen funksjoner vil ikke fungere.<br/>Vennligst rapporter dette til <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> utvikler.]]></string>\n    <string name=\"system_inject_fail_summary\">Systemets ramme-injeksjon mislyktes</string>\n    <string name=\"system_inject_fail\"><![CDATA[Dette er sjeldent og kan forårsakes av <b>Magisk</b> eller noen lavkvalitets magnetiske moduler.<br/>Prøv å deaktivere andre Magiske moduler enn Riru og LSPosed eller sende full logg til utviklere.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Feil i systemprop</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Noen nødvendige systemegenskaper kan slettes eller endres.<br/>Moduler kan godkjennes av og til.]]></string>\n    <string name=\"need_update\">Må oppdatere</string>\n    <string name=\"please_update_summary\">Vennligst installer den nyeste versjonen av LSPosed</string>\n    <string name=\"info_api_version\">API versjon</string>\n    <string name=\"info_framework_version\">Rammeverk versjon</string>\n    <string name=\"info_manager_package_name\">Lederpakkenavn (Automatic Translation)</string>\n    <string name=\"info_system_version\">System versjon</string>\n    <string name=\"info_device\">Enhet</string>\n    <string name=\"info_system_abi\">System ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Aktivert</string>\n    <string name=\"not_enabled\">Ikke aktivert</string>\n    <string name=\"supported\">Støttes</string>\n    <string name=\"unsupported\">Ustøttet</string>\n    <string name=\"android_version_unsatisfied\">Android-versjonen er ikke fornøyd</string>\n    <string name=\"crashed\">Krasjet</string>\n    <string name=\"mount_failed\">Montering mislyktes</string>\n    <string name=\"selinux_permissive\">SELinux er tillatt</string>\n    <string name=\"sepolicy_incorrect\">SELinux policy er feil</string>\n    <string name=\"update_lsposed\">Oppdateringen LSPosed</string>\n    <string name=\"update_lsposed_msg\">Bekreft oppdatering av LSPosed? Denne enheten vil starte på nytt etter fullført oppdatering</string>\n    <string name=\"info_copied\">Kopiert til utklippstavlen</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Velkommen til LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Du bruker den parasittiske manageren, som kan opprette snarvei eller fortsatt åpne fra varsling.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Du bruker parasittbehandleren, som kan åpnes fra varsling.</string>\n    <string name=\"create_shortcut\">Opprett snarvei</string>\n    <string name=\"never_show\">Vis aldri</string>\n    <string name=\"app_welcome\">Parasittisk leder anbefales</string>\n    <string name=\"app_welcome_summary\">LSPosed støtter nå systemparasittering for å unngå deteksjon, du kan åpne parasitic manager fra varsling. Det anbefales å avinstallere gjeldende applikasjon.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Lagre</string>\n    <string name=\"nav_item_logs_verbose\">Detaljert logg</string>\n    <string name=\"nav_item_logs_module\">Modullogger</string>\n    <string name=\"logs_saving\">Lagrer loggen, vennligst vent</string>\n    <string name=\"logs_saved\">Logger lagret</string>\n    <string name=\"logs_save_failed2\">Kunne ikke lagre:\\n%s</string>\n    <string name=\"menuClearLog\">Tøm logg nå</string>\n    <string name=\"logs_cleared\">Logg fjernet.</string>\n    <string name=\"scroll_top\">Rull til toppen</string>\n    <string name=\"loading\">Laster…</string>\n    <string name=\"scroll_bottom\">Rull til bunnen</string>\n    <string name=\"menuReload\">Reload</string>\n    <string name=\"logs_clear_failed_2\">Kunne ikke tømme loggen</string>\n    <string name=\"menu_enable_word_wrap\">Ordfarget farget farget</string>\n    <string name=\"enabled_verbose_log\">Detaljert logg aktivert</string>\n    <string name=\"disabled_verbose_log\">Detaljert logg deaktivert</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(ingen beskrivelse er gitt)</string>\n    <string name=\"warning_xposed_min_version\">Denne modulen krever en nyere Xposed versjon (%d) og kan dermed ikke aktiveres</string>\n    <string name=\"warning_target_version_higher\">Denne modulen er designet for en nyere Xposed-versjon (%d), og derfor kan det hende at enkelte funksjoner ikke fungerer</string>\n    <string name=\"no_min_version_specified\">Denne modulen spesifiserer ikke hvilken versjon av Xposed den trenger.</string>\n    <string name=\"warning_min_version_too_low\">Denne modulen var laget for Xposed versjon %1$d, men grunnet inkompatible endringer i versjon %2$der den blitt deaktivert</string>\n    <string name=\"warning_installed_on_external_storage\">Denne modulen kan ikke lastes fordi den er installert på SD-kortet, vennligst flytt den til intern lagring</string>\n    <string name=\"module_uninstall\">Avinstaller</string>\n    <string name=\"module_settings\">Modul innstillinger</string>\n    <string name=\"view_in_repo\">Vis i Repo</string>\n    <string name=\"module_uninstall_message\">Vil du avinstallere denne modulen?</string>\n    <string name=\"module_uninstalled\">Avinstallert %1$s</string>\n    <string name=\"module_uninstall_failed\">Avinstalleringen mislyktes</string>\n    <string name=\"add_module_to_user\">Legg til modul i brukeren</string>\n    <string name=\"module_installed\">La til %1$s som bruker %2$s</string>\n    <string name=\"module_install_failed\">Legge til modul mislyktes</string>\n    <string name=\"install_to_user\">Installer til bruker %s</string>\n    <string name=\"install_to_user_message\">Ønsker du installere %1$s til brukeren %2$s? Det anbefales å installere manuelt, tvinge installasjonen via LSPosed kan forårsake problemer.</string>\n    <string name=\"expand\">utvid</string>\n    <string name=\"collapse\">skjul</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Gjenoptimaliser</string>\n    <string name=\"compile_speed_msg\">Optimaliserer…</string>\n    <string name=\"compile_done\">Optimalisering fullført</string>\n    <string name=\"app_launch\">Start det</string>\n    <string name=\"compile_failed\">Optimalisering mislyktes: returverdien er tom</string>\n    <string name=\"compile_failed_with_info\">Optimalisering mislyktes: </string>\n    <string name=\"sort_by_name\">Navn på applikasjon</string>\n    <string name=\"sort_by_package_name\">Navn på pakke</string>\n    <string name=\"sort_by_install_time\">Installere tid</string>\n    <string name=\"sort_by_update_time\">Oppdaterings tid</string>\n    <string name=\"sort_reverse\">Revers</string>\n    <string name=\"menu_show_system_apps\">System apper</string>\n    <string name=\"menu_sort\">Sortering</string>\n    <string name=\"enable_module\">Aktiver modul</string>\n    <string name=\"no_scope_selected\">Du valgte ingen app. Fortsette?</string>\n    <string name=\"menu_show_games\">Spill</string>\n    <string name=\"menu_show_modules\">Moduler</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Kan ikke lagre områdeliste</string>\n    <string name=\"app_version\">Versjon: %1$s</string>\n    <string name=\"use_recommended\">Anbefalt</string>\n    <string name=\"no_scope_selected_has_recommended\">Du valgte ingen applikasjoner. Velg anbefalte apper?</string>\n    <string name=\"use_recommended_message\">Velg anbefalte apper?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modul er ikke aktivert enda</string>\n    <string name=\"requested_by_module\">Anbefalt</string>\n    <string name=\"update_available\">Oppdatering tilgjengelig: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s er deaktivert siden ingen app valgt.</string>\n    <string name=\"android_framework\">Systemets rammeverk</string>\n    <string name=\"menu_backup_and_restore\">Sikkerhetskopi</string>\n    <string name=\"menu_backup\">Sikkerhetskopi</string>\n    <string name=\"menu_restore\">Gjenopprett</string>\n    <string name=\"force_stop\">Tving avslutning</string>\n    <string name=\"force_stop_dlg_title\">Tving stopp?</string>\n    <string name=\"force_stop_dlg_text\">Hvis du tvangsstopper en app, kan den oppføre seg uventet.</string>\n    <string name=\"reboot_required\">Omstart kreves for at denne endringen skal gjelde</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Skjul</string>\n    <string name=\"deny_list\">%s er på denylist. Det kan ikke tre i kraft.</string>\n    <string name=\"deny_list_info\">På denylist</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Se i andre app</string>\n    <string name=\"module_app_info\">Info om app</string>\n    <string name=\"list_empty\">########\\\\\\\\_(∙)_\\/ ″\\nIngenting her</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Deaktiver detaljerte logger</string>\n    <string name=\"settings_disable_verbose_log_summary\">Rapporter problemer forespørsel om å inkludere detaljerte logger</string>\n    <string name=\"pure_black_dark_theme\">Svart mørkt tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Bruk det svarte temaet hvis mørkt tema er aktivert</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Sikkerhetskopiering og gjenoppretting</string>\n    <string name=\"settings_backup_summery\">Sikkerhetskopier modulliste og omfangslister.</string>\n    <string name=\"settings_restore_summery\">Gjenopprett modulliste og omfangslister.</string>\n    <string name=\"settings_backup\">Sikkerhetskopi</string>\n    <string name=\"settings_backup_failed2\">Klarte ikke å sikkerhetskopiere:\\n%s</string>\n    <string name=\"enable_documentui\">Aktiver DocumentUI</string>\n    <string name=\"settings_restore\">Gjenopprett</string>\n    <string name=\"settings_restore_failed2\">Kunne ikke gjenopprette:\\n%s</string>\n    <string name=\"group_network\">Nettverk</string>\n    <string name=\"dns_over_http\">DNS over HTTPS</string>\n    <string name=\"dns_over_http_summary\">Workaround DNS-forgiftning i noen nasjoner</string>\n    <string name=\"theme_color\">Tema farge</string>\n    <string name=\"theme_color_system\">System temafarge</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Tving apper til å vise programvelgerikoner</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Etter Android 10 er apper ikke tillatt å skjule programikonene sine. Slå av for å deaktivere denne systemfunksjonen.</string>\n    <string name=\"settings_group_system\">Systemadministrasjon</string>\n    <string name=\"settings_language\">Språk</string>\n    <string name=\"settings_translation_contributors\">Oversettelse bidragsytere</string>\n    <string name=\"settings_translation\">Delta i oversettelse</string>\n    <string name=\"settings_translation_summary\">Hjelp oss med å oversette %s til ditt språk</string>\n    <string name=\"settings_create_shortcut_summary\">Lag en snarvei som kan åpne parasitic manager</string>\n    <string name=\"settings_shortcut_pinned_hint\">Snarvei festet</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Den gjeldende standardstarteren støtter ikke pin-snarveier</string>\n    <string name=\"settings_enable_status_notification\">Statusvarsel</string>\n    <string name=\"settings_enable_status_notification_summary\">Vis et varsel som kan åpne parasitic manager</string>\n    <string name=\"disable_status_notification_error\">Ingen snarvei, kan ikke deaktivere varsling</string>\n    <string name=\"settings_update_channel\">Oppdater kanal</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">beta</string>\n    <string name=\"update_channel_nightly\">Nattlig bygg</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API-anropsbeskyttelse</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokker dynamisk lastet modulkode for å bruke Xposed API, dette kan ødelegge noen moduler, men gi fordeler for sikkerheten</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Lesmeg</string>\n    <string name=\"module_releases\">Utgivelser</string>\n    <string name=\"module_information\">Informasjon</string>\n    <string name=\"module_information_homepage\">Hjemmeside</string>\n    <string name=\"module_information_source_url\">Kildekode</string>\n    <string name=\"module_information_collaborators\">Samarbeidspartnere</string>\n    <string name=\"module_release_view_assets\">Eiendeler</string>\n    <string name=\"menu_open_in_browser\">Åpne i nettleser</string>\n    <string name=\"module_release_load_more\">Vis eldre versjoner</string>\n    <string name=\"module_release_no_more\">Ingen flere utgivelse</string>\n    <string name=\"repo_load_failed\">Kunne ikke laste modulrepo: %s</string>\n    <string name=\"sort_upgradable_first\">Oppgraderbar først</string>\n    <string name=\"installed\">Installert</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d nedlasting</item>\n        <item quantity=\"other\">%d nedlastinger</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Rød</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Lilla</string>\n    <string name=\"color_deep_purple\">Dyp lilla</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blå</string>\n    <string name=\"color_light_blue\">Lys blå</string>\n    <string name=\"color_cyan\">Turkis</string>\n    <string name=\"color_teal\">Blågrønn</string>\n    <string name=\"color_green\">Grønn</string>\n    <string name=\"color_light_green\">Lys grønn</string>\n    <string name=\"color_lime\">Limegrønn</string>\n    <string name=\"color_yellow\">Gul</string>\n    <string name=\"color_amber\">Rav</string>\n    <string name=\"color_orange\">Oransje</string>\n    <string name=\"color_deep_orange\">Dyp oransje</string>\n    <string name=\"color_brown\">Brun</string>\n    <string name=\"color_blue_grey\">Blå grå</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Przegląd</string>\n    <string name=\"Modules\">Moduły</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d moduł aktywowany</item>\n        <item quantity=\"few\">Aktywowano %d modułu/ów</item>\n        <item quantity=\"many\">Włączono %d moduły/ów</item>\n        <item quantity=\"other\">Włączono %d moduły/ów</item>\n    </plurals>\n    <string name=\"Logs\">Logi</string>\n    <string name=\"Settings\">Ustawienia</string>\n    <string name=\"feedback_or_suggestion\">Opinie lub sugestie</string>\n    <string name=\"About\">O aplikacji</string>\n    <string name=\"report_issue\">Zgłoś problem</string>\n    <string name=\"module_repo\">Repozytorium</string>\n    <string name=\"module_repo_up_to_date\">Wszystkie moduły są aktualne</string>\n    <string name=\"module_repo_published_time\">Opublikowano na stronie %s</string>\n    <string name=\"module_repo_updated_time\">Zaktualizowany na stronie %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d moduł możliwy do aktualizacji</item>\n        <item quantity=\"few\">%d moduły z możliwością aktualizacji</item>\n        <item quantity=\"many\">%d moduły z możliwością aktualizacji</item>\n        <item quantity=\"other\">%d modułów z możliwością aktualizacji</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Zobacz kod źródłowy na %1$s<br/>Dołącz do naszego kanału %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Adiker, xAzvest</string>\n    <string name=\"install\">Instaluj</string>\n    <string name=\"install_summary\">Dotknij, aby zainstalować LSPosed</string>\n    <string name=\"not_installed\">Nie zainstalowano</string>\n    <string name=\"not_install_summary\">LSPosed nie jest zainstalowany</string>\n    <string name=\"activated\">Aktywowano</string>\n    <string name=\"partial_activated\">Częściowo aktywowano</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy nie jest prawidłowo załadowane</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Niektóre funkcje nie będą działać.<br/>Proszę to zgłosić do twórcy <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magiska</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Wstrzyknięcie Frameworku Systemowego nie powiodło się</string>\n    <string name=\"system_inject_fail\"><![CDATA[Jest to rzadkie i może być spowodowane przez <b>Magiska</b> lub niektóre moduły Magisk niskiej jakości.<br/>Spróbuj wyłączyć moduły Magisk inne niż Riru i LSPosed lub przesłać pełny log programistom.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Nieprawidłowy system prop</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Niektóre niezbędne właściwości systemowe zostały usunięte lub zmienione.<br/>Moduły mogą czasami działać niepoprawnie.]]></string>\n    <string name=\"need_update\">Konieczna aktualizacja</string>\n    <string name=\"please_update_summary\">Proszę zainstalować najnowszą wersję LSPosed</string>\n    <string name=\"developer_warning_title\">Porady dla programisty modułów</string>\n    <string name=\"developer_warning_summary\">Wyłącz optymalizacje wdrażania w Android Studio lub użyj polecenia `gradlew installDebug`, aby zainstalować. W przeciwnym razie moduł APK nie zostanie zaktualizowany.</string>\n    <string name=\"info_api_version\">Wersja API</string>\n    <string name=\"info_framework_version\">Wersja frameworka</string>\n    <string name=\"info_manager_package_name\">Nazwa pakietu menedżera</string>\n    <string name=\"info_system_version\">Wersja systemu</string>\n    <string name=\"info_device\">Urządzenie</string>\n    <string name=\"info_system_abi\">ABI systemu</string>\n    <string name=\"info_dex2oat_wrapper\">Optymalizator Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Włączone</string>\n    <string name=\"not_enabled\">Wyłączone</string>\n    <string name=\"supported\">Obsługiwana strona</string>\n    <string name=\"unsupported\">Nieobsługiwane</string>\n    <string name=\"android_version_unsatisfied\">Niezadowolenie z wersji na Androida</string>\n    <string name=\"crashed\">Rozbita</string>\n    <string name=\"mount_failed\">Montowanie nie powiodło się</string>\n    <string name=\"selinux_permissive\">SELinux jest permisywny</string>\n    <string name=\"sepolicy_incorrect\">Zasady SELinuxa są nieprawidłowe</string>\n    <string name=\"update_lsposed\">Zaktualizuj LSPosed</string>\n    <string name=\"update_lsposed_msg\">Czy chcesz zatwierdzić aktualizację LSPosed? Urządzenie zostanie uruchomione ponownie po zakończeniu aktualizacji</string>\n    <string name=\"info_copied\">Skopiowane do schowka</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Witamy w LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Używasz menedżera pasożytów, który może utworzyć skrót na ekranie głównym lub nadal być otwieranym z powiadomienia.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Używasz menedżera pasożytów, który może być otwierany z powiadomienia.</string>\n    <string name=\"create_shortcut\">Utwórz skrót</string>\n    <string name=\"never_show\">Nigdy nie pokazuj</string>\n    <string name=\"app_welcome\">Zalecany Menedżer Pasożytowy</string>\n    <string name=\"app_welcome_summary\">LSPosed teraz obsługuje pasożytowanie systemowe by uniknąć wykrycia, możesz otworzyć menedżera pasożytów z okna powiadomień. Zalecane jest odinstalowanie obecnej aplikacji.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Zapisz</string>\n    <string name=\"nav_item_logs_verbose\">Logi verbose</string>\n    <string name=\"nav_item_logs_module\">Logi modułów</string>\n    <string name=\"logs_saving\">Zapisywanie dziennika, poczekaj</string>\n    <string name=\"logs_saved\">Zapisane logi</string>\n    <string name=\"logs_save_failed2\">Nie udało się zapisać:\\n%s</string>\n    <string name=\"menuClearLog\">Wyczyść log teraz</string>\n    <string name=\"logs_cleared\">Logi zostały pomyślnie wyczyszczone.</string>\n    <string name=\"scroll_top\">Przewiń na górę</string>\n    <string name=\"loading\">Ładowanie…</string>\n    <string name=\"scroll_bottom\">Przewiń na dół</string>\n    <string name=\"menuReload\">Przeładuj</string>\n    <string name=\"logs_clear_failed_2\">Nie udało się wyczyścić logu</string>\n    <string name=\"menu_enable_word_wrap\">Zawijanie wierszy</string>\n    <string name=\"enabled_verbose_log\">Logi verbose włączone</string>\n    <string name=\"disabled_verbose_log\">Logi verbose wyłączone</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(nie podano opisu)</string>\n    <string name=\"warning_xposed_min_version\">Ten moduł wymaga nowszej wersji Xposed (%d) przez co nie może być aktywowany</string>\n    <string name=\"warning_target_version_higher\">Ten moduł został zaprojektowany dla nowej wersji Xposed (%d) i dlatego niektóre funkcje mogą nie działać</string>\n    <string name=\"no_min_version_specified\">Ten moduł nie określa wymaganej wersji Xposed.</string>\n    <string name=\"warning_min_version_too_low\">Ten moduł został utworzony dla Xposed w wersji %1$d, ale ze względu na niekompatybilne zmiany w wersji %2$d, został wyłączony</string>\n    <string name=\"warning_installed_on_external_storage\">Ten moduł nie może być załadowany, ponieważ jest zainstalowany na karcie SD, przenieś go do pamięci wewnętrznej</string>\n    <string name=\"module_uninstall\">Odinstaluj</string>\n    <string name=\"module_settings\">Ustawienia modułu</string>\n    <string name=\"view_in_repo\">Wyświetl w repozytorium</string>\n    <string name=\"module_uninstall_message\">Czy chcesz odinstalować ten moduł?</string>\n    <string name=\"module_uninstalled\">Odinstalowano %1$s</string>\n    <string name=\"module_uninstall_failed\">Odinstalowanie nie powiodło się</string>\n    <string name=\"add_module_to_user\">Dodaj moduł do użytkownika</string>\n    <string name=\"module_installed\">Dodano %1$s do użytkownika %2$s</string>\n    <string name=\"module_install_failed\">Dodawanie modułu nie powiodło się</string>\n    <string name=\"install_to_user\">Zainstaluj dla użytkownika %s</string>\n    <string name=\"install_to_user_message\">Chcesz zainstalować %1$s dla użytkownika %2$s? Zalecana jest ręczna instalacja, wymuszanie instalacji przez LSPosed może powodować problemy.</string>\n    <string name=\"expand\">rozwiń</string>\n    <string name=\"collapse\">zwiń</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Ponownie zoptymalizuj</string>\n    <string name=\"compile_speed_msg\">Optymalizowanie…</string>\n    <string name=\"compile_done\">Optymalizacja zakończona</string>\n    <string name=\"app_launch\">Uruchom</string>\n    <string name=\"compile_failed\">Optymalizacja nie powiodła się: zwrócona wartość jest pusta</string>\n    <string name=\"compile_failed_with_info\">Optymalizacja nie powiodła się: </string>\n    <string name=\"sort_by_name\">Nazwa aplikacji</string>\n    <string name=\"sort_by_package_name\">Nazwa pakietu</string>\n    <string name=\"sort_by_install_time\">Czas instalacji</string>\n    <string name=\"sort_by_update_time\">Czas aktualizacji</string>\n    <string name=\"sort_reverse\">Odwróć</string>\n    <string name=\"menu_show_system_apps\">Aplikacje systemowe</string>\n    <string name=\"menu_sort\">Sortowanie</string>\n    <string name=\"enable_module\">Włącz moduł</string>\n    <string name=\"no_scope_selected\">Nie wybrałeś żadnej aplikacji. Kontynuować?</string>\n    <string name=\"menu_show_games\">Gry</string>\n    <string name=\"menu_show_modules\">Moduły</string>\n    <string name=\"menu_show_denylist\">Lista Odmów</string>\n    <string name=\"failed_to_save_scope_list\">Nie udało się zapisać listy zakresu</string>\n    <string name=\"app_version\">Wersja: %1$s</string>\n    <string name=\"menu_select\">Zaznacz</string>\n    <string name=\"use_recommended\">Zalecane</string>\n    <string name=\"no_scope_selected_has_recommended\">Nie wybrałeś żadnej aplikacji. Wybrać rekomendowane aplikacje?</string>\n    <string name=\"use_recommended_message\">Wybrać zalecane aplikacje?</string>\n    <string name=\"menu_select_all\">Wszystkie</string>\n    <string name=\"menu_select_none\">Żadne</string>\n    <string name=\"menu_auto_include\">Automatycznie</string>\n    <string name=\"module_is_not_activated_yet\">Moduł Xposed nie jest jeszcze aktywowany</string>\n    <string name=\"requested_by_module\">Zalecane</string>\n    <string name=\"update_available\">Dostępna aktualizacja: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Moduł %s został wyłączony, ponieważ nie wybrano żadnej aplikacji.</string>\n    <string name=\"android_framework\">System Framework</string>\n    <string name=\"menu_backup_and_restore\">Kopia zapasowa</string>\n    <string name=\"menu_backup\">Kopia zapasowa</string>\n    <string name=\"menu_restore\">Przywróć</string>\n    <string name=\"force_stop\">Wymuś zatrzymanie</string>\n    <string name=\"force_stop_dlg_title\">Wymusić zatrzymanie?</string>\n    <string name=\"force_stop_dlg_text\">Jeśli wymusisz zatrzymanie aplikacji, może ona zadziałać nieprawidłowo.</string>\n    <string name=\"reboot_required\">Restart jest wymagany aby ta zmiana została zastosowana</string>\n    <string name=\"reboot\">Restart</string>\n    <string name=\"menu_hide\">Ukryj</string>\n    <string name=\"deny_list\">%s jest na liście zablokowanych. Może nie odnieść skutku.</string>\n    <string name=\"deny_list_info\">Na zablokowanej liście</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Wyświetl w innej aplikacji</string>\n    <string name=\"module_app_info\">Informacje o aplikacji</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nNic tu nie ma</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Wyłącz logi</string>\n    <string name=\"settings_disable_verbose_log_summary\">Zgłaszanie problemów wymaga dołączenia pełnych dzienników</string>\n    <string name=\"settings_enable_log_watchdog\">Włącz nadzór dziennika</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Nadzór dziennika LSPosed modyfikuje właściwości systemowe, które mogą być wykorzystywane do wykrywania LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Czarny ciemny motyw</string>\n    <string name=\"pure_black_dark_theme_summary\">Użyj czarnego motywu, jeśli ciemny motyw jest włączony</string>\n    <string name=\"settings_group_theme\">Motyw</string>\n    <string name=\"settings_backup_and_restore\">Kopia zapasowa i przywracanie</string>\n    <string name=\"settings_backup_summery\">Kopia zapasowa listy modułów i zakresów.</string>\n    <string name=\"settings_restore_summery\">Przywrócenie listy modułów i listy zakresów.</string>\n    <string name=\"settings_backup\">Kopia zapasowa</string>\n    <string name=\"settings_backup_failed2\">Nie udało się utworzyć kopii zapasowej:\\n%s</string>\n    <string name=\"enable_documentui\">Proszę włączyć DocumentUI</string>\n    <string name=\"settings_restore\">Przywróć</string>\n    <string name=\"settings_restore_failed2\">Nie udało się przywrócić:\\n%s</string>\n    <string name=\"group_network\">Sieć</string>\n    <string name=\"dns_over_http\">DNS przez HTTPS</string>\n    <string name=\"dns_over_http_summary\">Obejście zatruwania DNS w niektórych krajach</string>\n    <string name=\"theme_color\">Kolor motywu</string>\n    <string name=\"theme_color_system\">Kolor akcentu systemowego</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Wymuś aplikacjom pokazywanie ikon launchera</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Po Android 10 aplikacje nie mogą ukrywać swoich ikon launchera. Wyłącz przełącznik, aby zablokować tę funkcję systemową.</string>\n    <string name=\"settings_group_system\">System</string>\n    <string name=\"settings_language\">Język</string>\n    <string name=\"settings_translation_contributors\">Tłumacze</string>\n    <string name=\"settings_translation\">Weź udział w tłumaczeniu</string>\n    <string name=\"settings_translation_summary\">Pomóż nam przetłumaczyć %s na Twój język</string>\n    <string name=\"settings_create_shortcut_summary\">Utwórz skrót, za pomocą którego otworzysz menadżera \\\"pasożytów\\\"</string>\n    <string name=\"settings_shortcut_pinned_hint\">Skrót przypięty</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Bieżący domyślny launcher nie wspiera skrótów pinezkowych</string>\n    <string name=\"settings_enable_status_notification\">Status powiadomień</string>\n    <string name=\"settings_enable_status_notification_summary\">Pokazuj powiadomienie, za pomocą którego otworzysz menadżera pasożytów</string>\n    <string name=\"disable_status_notification_error\">Brak skrótu, nie można wyłączyć powiadomienia</string>\n    <string name=\"settings_update_channel\">Kanał aktualizacji</string>\n    <string name=\"update_channel_stable\">Stabilny</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Wersja nocna</string>\n    <string name=\"settings_xposed_api_call_protection\">Ochrona wywołania Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokuj dynamicznie ładowany kod modułu do używania API Xposed, wzmocni to ochronę kosztem możliwego uszkodzenia niektórych modułów</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Wersje</string>\n    <string name=\"module_information\">Informacje</string>\n    <string name=\"module_information_homepage\">Strona główna</string>\n    <string name=\"module_information_source_url\">Kod źródłowy</string>\n    <string name=\"module_information_collaborators\">Współtwórcy</string>\n    <string name=\"module_release_view_assets\">Assety</string>\n    <string name=\"menu_open_in_browser\">Otwórz w przeglądarce</string>\n    <string name=\"module_release_load_more\">Pokaż starsze wersje</string>\n    <string name=\"module_release_no_more\">Nie ma więcej wersji</string>\n    <string name=\"repo_load_failed\">Nie udało się załadować repozytorium modułu: %s</string>\n    <string name=\"sort_upgradable_first\">Możliwość uaktualnienia w pierwszej kolejności</string>\n    <string name=\"installed\">Zainstalowane</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d pobierz</item>\n        <item quantity=\"few\">%d pobieranie</item>\n        <item quantity=\"many\">%d pobieranie</item>\n        <item quantity=\"other\">%d pobrania</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Czerwony</string>\n    <string name=\"color_pink\">Różowy</string>\n    <string name=\"color_purple\">Fioletowy</string>\n    <string name=\"color_deep_purple\">Ciemny fioletowy</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Niebieski</string>\n    <string name=\"color_light_blue\">Jasnoniebieski</string>\n    <string name=\"color_cyan\">Cyjanowy</string>\n    <string name=\"color_teal\">Turkusowy</string>\n    <string name=\"color_green\">Zielony</string>\n    <string name=\"color_light_green\">Jasnozielony</string>\n    <string name=\"color_lime\">Limonkowy</string>\n    <string name=\"color_yellow\">Żółty</string>\n    <string name=\"color_amber\">Bursztynowy</string>\n    <string name=\"color_orange\">Pomarańczowy</string>\n    <string name=\"color_deep_orange\">Ciemny pomarańczowy</string>\n    <string name=\"color_brown\">Brązowy</string>\n    <string name=\"color_blue_grey\">Niebiesko szary</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Resumo</string>\n    <string name=\"Modules\">Módulos</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d módulo ativo</item>\n        <item quantity=\"other\">%d módulos ativos</item>\n    </plurals>\n    <string name=\"Logs\">Registros</string>\n    <string name=\"Settings\">Configurações</string>\n    <string name=\"feedback_or_suggestion\">Comentários e Sugestões</string>\n    <string name=\"About\">Sobre</string>\n    <string name=\"report_issue\">Relatar problema</string>\n    <string name=\"module_repo\">Repositório</string>\n    <string name=\"module_repo_up_to_date\">Todos os módulos estão atualizados</string>\n    <string name=\"module_repo_published_time\">Publicado em %s</string>\n    <string name=\"module_repo_updated_time\">Atualizado em %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d módulo para atualizar</item>\n        <item quantity=\"other\">%d módulos para atualizar</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Veja o código-fonte no %1$s<br/>Entre em nosso canal no %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">VD171, Dan, Hitalo, Luiz Renato, PedriR19</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"install_summary\">Toque para instalar o LSPosed</string>\n    <string name=\"not_installed\">Não instalado</string>\n    <string name=\"not_install_summary\">O LSPosed não está instalado</string>\n    <string name=\"activated\">Ativado</string>\n    <string name=\"partial_activated\">Parcialmente Ativado</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy não está devidamente carregado</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Algumas características não funcionarão.<br/>Por favor informe <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> desenvolvedor.]]></string>\n    <string name=\"system_inject_fail_summary\">Falha na injeção ao Framework do Sistema</string>\n    <string name=\"system_inject_fail\"><![CDATA[Isso é raro e pode ter sido causado pelo <b>Magisk</b> ou por algum módulo do Magisk de baixa qualidade.<br/>Por favor, tente desativar os módulos do Magisk, exceto o LSPosed (e o Riru, caso esteja em uso). Ou envie todos os registros para os desenvolvedores.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Propriedade do sistema incorreta</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Algumas propriedades necessárias do sistema foram excluídas ou modificadas.<br/>Alguns módulos podem falhar ocasionalmente.]]></string>\n    <string name=\"need_update\">Necessário atualizar</string>\n    <string name=\"please_update_summary\">Por favor, instale a versão mais recente do LSPosed</string>\n    <string name=\"info_api_version\">Versão da API</string>\n    <string name=\"info_framework_version\">Versão do Framework</string>\n    <string name=\"info_manager_package_name\">Nome do pacote do gestor</string>\n    <string name=\"info_system_version\">Versão do Sistema</string>\n    <string name=\"info_device\">Aparelho</string>\n    <string name=\"info_system_abi\">Interfaces Binárias do Sistema</string>\n    <string name=\"info_dex2oat_wrapper\">Otimização Encapsulamento Dex</string>\n    <string name=\"enabled\">Ativado</string>\n    <string name=\"not_enabled\">Desativado</string>\n    <string name=\"supported\">Compatível</string>\n    <string name=\"unsupported\">Incompatível</string>\n    <string name=\"android_version_unsatisfied\">Versão do Android insuficiente</string>\n    <string name=\"crashed\">Falhou</string>\n    <string name=\"mount_failed\">Falha na montagem</string>\n    <string name=\"selinux_permissive\">O SELinux está permissivo</string>\n    <string name=\"sepolicy_incorrect\">A política do SELinux está incorreta</string>\n    <string name=\"update_lsposed\">Atualizar o LSPosed</string>\n    <string name=\"update_lsposed_msg\">Confirmar atualização do LSPosed? O aparelho irá reiniciar após completar a atualização</string>\n    <string name=\"info_copied\">Copiado para a área de transferência</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Bem-vindo ao LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Você está usando o gerenciador parasita, você pode criar o atalho ou então abrir a partir da notificação.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Você está usando o gerenciador parasita, ele pode ser aberto a partir da notificação.</string>\n    <string name=\"create_shortcut\">Criar Atalho do Gerenciador Parasita</string>\n    <string name=\"never_show\">Não mostrar novamente</string>\n    <string name=\"app_welcome\">É recomendado o gerenciador parasita</string>\n    <string name=\"app_welcome_summary\">O LSPosed agora oferece suporte a parasitização do sistema para evitar detecções, você pode abrir o gerenciador parasita a partir da notificação. É recomendado desinstalar o aplicativo atual.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Salvar</string>\n    <string name=\"nav_item_logs_verbose\">Registros Detalhados</string>\n    <string name=\"nav_item_logs_module\">Registros dos Módulos</string>\n    <string name=\"logs_saving\">A guardar o registo, aguarde</string>\n    <string name=\"logs_saved\">Registros salvos</string>\n    <string name=\"logs_save_failed2\">Falha ao salvar:\\n%s</string>\n    <string name=\"menuClearLog\">Limpar Registros</string>\n    <string name=\"logs_cleared\">Registro limpo com sucesso.</string>\n    <string name=\"scroll_top\">Rolar para o topo</string>\n    <string name=\"loading\">Carregando…</string>\n    <string name=\"scroll_bottom\">Rolar para baixo</string>\n    <string name=\"menuReload\">Recarregar</string>\n    <string name=\"logs_clear_failed_2\">Falha ao limpar os registros</string>\n    <string name=\"menu_enable_word_wrap\">Quebra de Linha</string>\n    <string name=\"enabled_verbose_log\">Registros detalhados ativados</string>\n    <string name=\"disabled_verbose_log\">Registros detalhados desativados</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(nenhuma descrição fornecida)</string>\n    <string name=\"warning_xposed_min_version\">Este módulo exige uma versão mais recente do Xposed (%d). Por isso, não poderá ser ativado</string>\n    <string name=\"warning_target_version_higher\">Este módulo foi concebido para uma versão mais recente do Xposed (%d) e, portanto, algumas funcionalidades podem não funcionar</string>\n    <string name=\"no_min_version_specified\">Este módulo não especifica qual a versão necessária do Xposed.</string>\n    <string name=\"warning_min_version_too_low\">Este módulo foi criado para a versão %1$d do Xposed. Mas, devido mudanças incompatíveis na versão %2$d, ele foi desativado</string>\n    <string name=\"warning_installed_on_external_storage\">Este módulo não pode ser carregado porque está instalado no cartão de memória. Por favor, mova para o armazenamento interno</string>\n    <string name=\"module_uninstall\">Desinstalar</string>\n    <string name=\"module_settings\">Abrir módulo</string>\n    <string name=\"view_in_repo\">Ver no Repositório</string>\n    <string name=\"module_uninstall_message\">Você deseja desinstalar este módulo?</string>\n    <string name=\"module_uninstalled\">%1$s desinstalado</string>\n    <string name=\"module_uninstall_failed\">Falha ao desinstalar</string>\n    <string name=\"add_module_to_user\">Adicionar módulo no usuário</string>\n    <string name=\"module_installed\">%1$s foi adicionado no usuário %2$s</string>\n    <string name=\"module_install_failed\">Falha ao adicionar módulo</string>\n    <string name=\"install_to_user\">Instalar no usuário %s</string>\n    <string name=\"install_to_user_message\">Deseja instalar %1$s no usuário %2$s? É recomendado instalar manualmente. Forçar a instalação via LSPosed pode causar problemas.</string>\n    <string name=\"expand\">expandir</string>\n    <string name=\"collapse\">recolher</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Re-otimizar</string>\n    <string name=\"compile_speed_msg\">Otimizando…</string>\n    <string name=\"compile_done\">Otimização completa</string>\n    <string name=\"app_launch\">Abrir app</string>\n    <string name=\"compile_failed\">A otimização falhou: não houve nenhum retorno</string>\n    <string name=\"compile_failed_with_info\">Falha na otimização: </string>\n    <string name=\"sort_by_name\">Nome do Módulo</string>\n    <string name=\"sort_by_package_name\">Nome do Pacote</string>\n    <string name=\"sort_by_install_time\">Data de Instalação</string>\n    <string name=\"sort_by_update_time\">Data de Atualização</string>\n    <string name=\"sort_reverse\">Inverter</string>\n    <string name=\"menu_show_system_apps\">Apps do Sistema</string>\n    <string name=\"menu_sort\">Ordenar</string>\n    <string name=\"enable_module\">Ativar módulo</string>\n    <string name=\"no_scope_selected\">Você não selecionou nenhum aplicativo. Deseja continuar?</string>\n    <string name=\"menu_show_games\">Jogos</string>\n    <string name=\"menu_show_modules\">Módulos</string>\n    <string name=\"menu_show_denylist\">Lista de Negação</string>\n    <string name=\"failed_to_save_scope_list\">Falha ao salvar lista de escopos</string>\n    <string name=\"app_version\">Versão: %1$s</string>\n    <string name=\"use_recommended\">Recomendações</string>\n    <string name=\"no_scope_selected_has_recommended\">Você não selecionou nenhum aplicativo. Deseja selecionar os aplicativos recomendados?</string>\n    <string name=\"use_recommended_message\">Selecionar os aplicativos recomendados?</string>\n    <string name=\"module_is_not_activated_yet\">O módulo ainda não está ativo</string>\n    <string name=\"requested_by_module\">Recomendações</string>\n    <string name=\"update_available\">Atualização disponível: %1$s</string>\n    <string name=\"module_disabled_no_selection\">O módulo %s foi desativado porque nenhum aplicativo foi selecionado.</string>\n    <string name=\"android_framework\">Framework do Sistema</string>\n    <string name=\"menu_backup_and_restore\">Fazer Backup</string>\n    <string name=\"menu_backup\">Fazer Backup</string>\n    <string name=\"menu_restore\">Restaurar Backup</string>\n    <string name=\"force_stop\">Forçar parada</string>\n    <string name=\"force_stop_dlg_title\">Forçar parada?</string>\n    <string name=\"force_stop_dlg_text\">Se forçar a parada de um aplicativo, ele pode apresentar falhas.</string>\n    <string name=\"reboot_required\">Necessário reiniciar para aplicar alteração</string>\n    <string name=\"reboot\">Reiniciar</string>\n    <string name=\"menu_hide\">Esconder</string>\n    <string name=\"deny_list\">%s está na lista de negação. Pode não ter efeito.</string>\n    <string name=\"deny_list_info\">Na lista de negação</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Ver na Play Store</string>\n    <string name=\"module_app_info\">Informações do app</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nNada encontrado</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Desativar registros detalhados</string>\n    <string name=\"settings_disable_verbose_log_summary\">Relate o problema para incluir os registros detalhados</string>\n    <string name=\"pure_black_dark_theme\">Usar Tema Preto Puro</string>\n    <string name=\"pure_black_dark_theme_summary\">Use o tema preto puro se o tema noturno estiver ativo</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Backup e Restauração</string>\n    <string name=\"settings_backup_summery\">Faz uma cópia de segurança da lista de módulos e escopos</string>\n    <string name=\"settings_restore_summery\">Faz a restauração de uma cópia de segurança da lista de módulos e escopos</string>\n    <string name=\"settings_backup\">Fazer Backup</string>\n    <string name=\"settings_backup_failed2\">Falha ao fazer backup:\\n%s</string>\n    <string name=\"enable_documentui\">Por favor, ative DocumentUI</string>\n    <string name=\"settings_restore\">Restaurar Backup</string>\n    <string name=\"settings_restore_failed2\">Falha ao restaurar:\\n%s</string>\n    <string name=\"group_network\">Rede</string>\n    <string name=\"dns_over_http\">Ativar DNS sobre HTTPS</string>\n    <string name=\"dns_over_http_summary\">Solução alternativa para envenenamento de DNS em alguns países</string>\n    <string name=\"theme_color\">Cor do Tema</string>\n    <string name=\"theme_color_system\">Cor de destaque do sistema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Forçar exibição de ícones dos apps</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Após o Android 10, alguns apps não estão autorizados a ocultarem seus ícones. Desative esta opção para desligar este recurso</string>\n    <string name=\"settings_group_system\">Sistema</string>\n    <string name=\"settings_language\">Idioma</string>\n    <string name=\"settings_translation_contributors\">Tradutores</string>\n    <string name=\"settings_translation\">Participe das Traduções</string>\n    <string name=\"settings_translation_summary\">Ajude a traduzir o %s para o Português 🇵🇹🇧🇷</string>\n    <string name=\"settings_create_shortcut_summary\">Criar um atalho para abrir gerenciador parasita</string>\n    <string name=\"settings_shortcut_pinned_hint\">Atalho com alfinete</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">O lançador padrão actual não suporta atalhos de pinos</string>\n    <string name=\"settings_enable_status_notification\">Notificação de estado</string>\n    <string name=\"settings_enable_status_notification_summary\">Mostrar uma notificação que possa abrir o gerenciador parasita</string>\n    <string name=\"disable_status_notification_error\">Nenhum atalho, não é possível desativar a notificação</string>\n    <string name=\"settings_update_channel\">Canal de Atualizações</string>\n    <string name=\"update_channel_stable\">Estável</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly</string>\n    <string name=\"settings_xposed_api_call_protection\">Proteção da API do Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Bloquear módulos carregados dinamicamente de usarem a API do Xposed, isso pode travar alguns módulos, mas melhora a segurança</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Leia-me</string>\n    <string name=\"module_releases\">Versões</string>\n    <string name=\"module_information\">Informações</string>\n    <string name=\"module_information_homepage\">Website</string>\n    <string name=\"module_information_source_url\">Código-Fonte</string>\n    <string name=\"module_information_collaborators\">Colaboradores</string>\n    <string name=\"module_release_view_assets\">Conteúdos</string>\n    <string name=\"menu_open_in_browser\">Abrir no navegador</string>\n    <string name=\"module_release_load_more\">Mostrar versões anteriores</string>\n    <string name=\"module_release_no_more\">Não há mais versões</string>\n    <string name=\"repo_load_failed\">Falha ao carregar o repositório de módulos: %s</string>\n    <string name=\"sort_upgradable_first\">Atualizável Primeiro</string>\n    <string name=\"installed\">Instalado</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d baixado</item>\n        <item quantity=\"other\">%d baixados</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Vermelho</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Roxo</string>\n    <string name=\"color_deep_purple\">Roxo Escuro</string>\n    <string name=\"color_indigo\">Azul-anil</string>\n    <string name=\"color_blue\">Azul</string>\n    <string name=\"color_light_blue\">Azul Claro</string>\n    <string name=\"color_cyan\">Ciano</string>\n    <string name=\"color_teal\">Verde-azulado</string>\n    <string name=\"color_green\">Verde</string>\n    <string name=\"color_light_green\">Verde Claro</string>\n    <string name=\"color_lime\">Verde-limão</string>\n    <string name=\"color_yellow\">Amarelo</string>\n    <string name=\"color_amber\">Âmbar</string>\n    <string name=\"color_orange\">Laranja</string>\n    <string name=\"color_deep_orange\">Laranja Escuro</string>\n    <string name=\"color_brown\">Marrom</string>\n    <string name=\"color_blue_grey\">Azul-acinzentado</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Início</string>\n    <string name=\"Modules\">Módulos</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d módulo ativo</item>\n        <item quantity=\"other\">%d módulos ativos</item>\n    </plurals>\n    <string name=\"Logs\">Registros</string>\n    <string name=\"Settings\">Configurações</string>\n    <string name=\"feedback_or_suggestion\">Feedback ou sugestão</string>\n    <string name=\"About\">Sobre</string>\n    <string name=\"report_issue\">Relatar problema</string>\n    <string name=\"module_repo\">Repositório</string>\n    <string name=\"module_repo_up_to_date\">Todos os módulos estão atualizados</string>\n    <string name=\"module_repo_published_time\">Publicado em %s</string>\n    <string name=\"module_repo_updated_time\">Atualizado em %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d módulo para atualizar</item>\n        <item quantity=\"other\">%d módulos para atualizar</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Veja o código-fonte no %1$s<br/>Participe do nosso canal do %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">VD171, Dan, Hitalo, Luiz Renato, PedriR19 e Igor</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"install_summary\">Toque para instalar o LSPosed</string>\n    <string name=\"not_installed\">Não instalado</string>\n    <string name=\"not_install_summary\">O LSPosed não está instalado</string>\n    <string name=\"activated\">Ativado</string>\n    <string name=\"partial_activated\">Parcialmente ativado</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy não está devidamente carregado</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Alguns recursos não funcionarão.<br/>Por favor, relate isso ao desenvolvedor do <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Falha na injeção do Framework do Sistema</string>\n    <string name=\"system_inject_fail\"><![CDATA[Isso é raro e pode ter sido causado pelo <b>Magisk</b> ou por algum módulo do Magisk de baixa qualidade.<br/>Por favor, tente desativar os módulos do Magisk, exceto o LSPosed (e o Riru, caso esteja em uso). Ou envie todos os registros para os desenvolvedores.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Propriedade do sistema incorreta</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Algumas propriedades necessárias do sistema foram excluídas ou modificadas.<br/>Alguns módulos podem falhar ocasionalmente.]]></string>\n    <string name=\"need_update\">Necessário atualizar</string>\n    <string name=\"please_update_summary\">Por favor, instale a versão mais recente do LSPosed.</string>\n    <string name=\"developer_warning_title\">Dicas para desenvolvedores de módulos</string>\n    <string name=\"developer_warning_summary\">Por favor, desative a implantação de otimizações no Android Studio ou use o comando `gradlew installDebug` para instalar. Caso contrário, o módulo APK não será atualizado.</string>\n    <string name=\"info_api_version\">Versão da API</string>\n    <string name=\"info_framework_version\">Versão do framework</string>\n    <string name=\"info_manager_package_name\">Nome do pacote do gerenciador</string>\n    <string name=\"info_system_version\">Versão do sistema</string>\n    <string name=\"info_device\">Dispositivo</string>\n    <string name=\"info_system_abi\">ABI do sistema</string>\n    <string name=\"info_dex2oat_wrapper\">Invólucro do otimizador dex</string>\n    <string name=\"enabled\">Ativado</string>\n    <string name=\"not_enabled\">Desativado</string>\n    <string name=\"supported\">Compatível</string>\n    <string name=\"unsupported\">Incompatível</string>\n    <string name=\"android_version_unsatisfied\">Versão do Android insuficiente</string>\n    <string name=\"crashed\">Falhou</string>\n    <string name=\"mount_failed\">Falha na montagem</string>\n    <string name=\"selinux_permissive\">O SELinux está permissivo</string>\n    <string name=\"sepolicy_incorrect\">A política do SELinux está incorreta</string>\n    <string name=\"update_lsposed\">Atualizar o LSPosed</string>\n    <string name=\"update_lsposed_msg\">Confirmar atualização do LSPosed? O dispositivo irá reiniciar após completar a atualização.</string>\n    <string name=\"info_copied\">Copiado para a área de transferência</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Bem-vindo ao LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Você está usando o gerenciador parasita, você pode criar o atalho ou então abrir a partir da notificação.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Você está usando o gerenciador parasita, ele pode ser aberto a partir da notificação.</string>\n    <string name=\"create_shortcut\">Criar atalho do gerenciador parasita</string>\n    <string name=\"never_show\">Não mostrar novamente</string>\n    <string name=\"app_welcome\">Gerenciador parasita recomendado</string>\n    <string name=\"app_welcome_summary\">O LSPosed agora oferece suporte a parasitização do sistema para evitar detecções, você pode abrir o gerenciador parasita a partir da notificação. É recomendado desinstalar o app atual.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Salvar</string>\n    <string name=\"nav_item_logs_verbose\">Registros detalhados</string>\n    <string name=\"nav_item_logs_module\">Registros dos módulos</string>\n    <string name=\"logs_saving\">Salvando registro, aguarde.</string>\n    <string name=\"logs_saved\">Registros salvos</string>\n    <string name=\"logs_save_failed2\">Falha ao salvar:\\n%s</string>\n    <string name=\"menuClearLog\">Limpar registros</string>\n    <string name=\"logs_cleared\">Registros limpo com sucesso.</string>\n    <string name=\"scroll_top\">Rolar para cima</string>\n    <string name=\"loading\">Carregando…</string>\n    <string name=\"scroll_bottom\">Rolar para baixo</string>\n    <string name=\"menuReload\">Recarregar</string>\n    <string name=\"logs_clear_failed_2\">Falha ao limpar os registros</string>\n    <string name=\"menu_enable_word_wrap\">Quebra de linha</string>\n    <string name=\"enabled_verbose_log\">Registros detalhados ativados</string>\n    <string name=\"disabled_verbose_log\">Registros detalhados desativados</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(nenhuma descrição fornecida)</string>\n    <string name=\"warning_xposed_min_version\">Este módulo exige uma versão mais recente do LSPosed (%d). Por isso, não poderá ser ativado.</string>\n    <string name=\"warning_target_version_higher\">Este módulo foi projetado para uma versão mais recente do LSPosed (%d). Por isso, algumas funcionalidades podem não funcionar.</string>\n    <string name=\"no_min_version_specified\">Este módulo não especifica qual a versão necessária do LSPosed.</string>\n    <string name=\"warning_min_version_too_low\">Este módulo foi criado para a versão %1$d do LSPosed, mas devido a alterações incompatíveis na versão %2$d, ele foi desativado.</string>\n    <string name=\"warning_installed_on_external_storage\">Este módulo não pode ser carregado porque está instalado no cartão de memória. Por favor, mova para o armazenamento interno.</string>\n    <string name=\"module_uninstall\">Desinstalar</string>\n    <string name=\"module_settings\">Abrir módulo</string>\n    <string name=\"view_in_repo\">Ver no repositório</string>\n    <string name=\"module_uninstall_message\">Deseja desinstalar este módulo?</string>\n    <string name=\"module_uninstalled\">%1$s desinstalado</string>\n    <string name=\"module_uninstall_failed\">Falha ao desinstalar</string>\n    <string name=\"add_module_to_user\">Adicionar módulo ao usuário</string>\n    <string name=\"module_installed\">%1$s foi adicionado ao usuário %2$s</string>\n    <string name=\"module_install_failed\">Falha ao adicionar módulo</string>\n    <string name=\"install_to_user\">Instalar no usuário %s</string>\n    <string name=\"install_to_user_message\">Deseja instalar %1$s no usuário %2$s? É recomendado instalar manualmente. Forçar a instalação via LSPosed pode causar problemas.</string>\n    <string name=\"expand\">expandir</string>\n    <string name=\"collapse\">recolher</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Re-otimizar</string>\n    <string name=\"compile_speed_msg\">Otimizando…</string>\n    <string name=\"compile_done\">Otimização completa</string>\n    <string name=\"app_launch\">Abrir app</string>\n    <string name=\"compile_failed\">A otimização falhou: não houve nenhum retorno</string>\n    <string name=\"compile_failed_with_info\">Falha na otimização: </string>\n    <string name=\"sort_by_name\">Nome do app</string>\n    <string name=\"sort_by_package_name\">Nome do pacote</string>\n    <string name=\"sort_by_install_time\">Data de instalação</string>\n    <string name=\"sort_by_update_time\">Data de atualização</string>\n    <string name=\"sort_reverse\">Inverter</string>\n    <string name=\"menu_show_system_apps\">Apps do sistema</string>\n    <string name=\"menu_sort\">Ordenar</string>\n    <string name=\"enable_module\">Ativar módulo</string>\n    <string name=\"no_scope_selected\">Você não selecionou nenhum app. Deseja continuar?</string>\n    <string name=\"menu_show_games\">Jogos</string>\n    <string name=\"menu_show_modules\">Módulos</string>\n    <string name=\"menu_show_denylist\">Lista de negação</string>\n    <string name=\"failed_to_save_scope_list\">Falha ao salvar lista de escopos</string>\n    <string name=\"app_version\">Versão: %1$s</string>\n    <string name=\"menu_select\">Selecionar</string>\n    <string name=\"use_recommended\">Recomendações</string>\n    <string name=\"no_scope_selected_has_recommended\">Você não selecionou nenhum app. Deseja selecionar os apps recomendados?</string>\n    <string name=\"use_recommended_message\">Selecionar os apps recomendados?</string>\n    <string name=\"menu_select_all\">Todos</string>\n    <string name=\"menu_select_none\">Nenhum</string>\n    <string name=\"menu_auto_include\">Auto-incluir</string>\n    <string name=\"module_is_not_activated_yet\">O módulo ainda não está ativo</string>\n    <string name=\"requested_by_module\">Recomendações</string>\n    <string name=\"update_available\">Atualização disponível: %1$s</string>\n    <string name=\"module_disabled_no_selection\">O módulo %s foi desativado porque nenhum app foi selecionado.</string>\n    <string name=\"android_framework\">Framework do Sistema</string>\n    <string name=\"menu_backup_and_restore\">Fazer backup</string>\n    <string name=\"menu_backup\">Fazer backup</string>\n    <string name=\"menu_restore\">Restaurar backup</string>\n    <string name=\"force_stop\">Forçar parada</string>\n    <string name=\"force_stop_dlg_title\">Forçar parada?</string>\n    <string name=\"force_stop_dlg_text\">Se você forçar a parada de um app, problemas podem ocorrer.</string>\n    <string name=\"reboot_required\">Necessário reiniciar para aplicar a alteração</string>\n    <string name=\"reboot\">Reiniciar</string>\n    <string name=\"menu_hide\">Ocultar</string>\n    <string name=\"deny_list\">%s está na lista de negação. Pode não ter efeito.</string>\n    <string name=\"deny_list_info\">Na lista de negação</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Ver em outro app</string>\n    <string name=\"module_app_info\">Informações do app</string>\n    <string name=\"list_empty\">¯\\\\_(ツ)_\\/¯\\nNada aqui</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Desativar registros detalhados</string>\n    <string name=\"settings_disable_verbose_log_summary\">Relate o problema para incluir os registros detalhados</string>\n    <string name=\"settings_enable_log_watchdog\">Ativar monitoramento de registro</string>\n    <string name=\"settings_enable_log_watchdog_summary\">O monitoramento de registro do LSPosed modifica as propriedades do sistema, que podem ser exploradas para detectar o LSPosed.</string>\n    <string name=\"pure_black_dark_theme\">Tema preto puro</string>\n    <string name=\"pure_black_dark_theme_summary\">Use o tema preto puro se o tema escuro estiver ativo</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Backup e restauração</string>\n    <string name=\"settings_backup_summery\">Faça um backup da lista de módulos e escopos</string>\n    <string name=\"settings_restore_summery\">Restaure a lista de módulos e escopos</string>\n    <string name=\"settings_backup\">Fazer backup</string>\n    <string name=\"settings_backup_failed2\">Falha ao fazer backup:\\n%s</string>\n    <string name=\"enable_documentui\">Por favor, ative o DocumentUI.</string>\n    <string name=\"settings_restore\">Restaurar backup</string>\n    <string name=\"settings_restore_failed2\">Falha ao restaurar:\\n%s</string>\n    <string name=\"group_network\">Rede</string>\n    <string name=\"dns_over_http\">DNS sobre HTTPS</string>\n    <string name=\"dns_over_http_summary\">Solução alternativa para envenenamento de DNS em alguns países</string>\n    <string name=\"theme_color\">Cor do tema</string>\n    <string name=\"theme_color_system\">Cor de destaque do sistema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Forçar exibição de ícones dos apps</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Após o Android 10, alguns apps não estão autorizados a ocultarem seus ícones. Desative esta opção para desligar este recurso.</string>\n    <string name=\"settings_group_system\">Sistema</string>\n    <string name=\"settings_language\">Idioma</string>\n    <string name=\"settings_translation_contributors\">Tradutores</string>\n    <string name=\"settings_translation\">Participe das traduções</string>\n    <string name=\"settings_translation_summary\">Ajude-nos a traduzir o %s para o Português (Brasil)</string>\n    <string name=\"settings_create_shortcut_summary\">Crie um atalho para abrir o gerenciador parasita</string>\n    <string name=\"settings_shortcut_pinned_hint\">Atalho fixado</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">A launcher padrão atual não suporta atalhos fixados</string>\n    <string name=\"settings_enable_status_notification\">Notificação de estado</string>\n    <string name=\"settings_enable_status_notification_summary\">Mostre uma notificação que possa abrir o gerenciador parasita</string>\n    <string name=\"disable_status_notification_error\">Nenhum atalho, não é possível desativar a notificação.</string>\n    <string name=\"settings_update_channel\">Canal de atualizações</string>\n    <string name=\"update_channel_stable\">Estável</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly</string>\n    <string name=\"settings_xposed_api_call_protection\">Proteção da API do Xposed</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Bloqueie módulos carregados dinamicamente de usarem a API do Xposed. Isso pode quebrar alguns módulos, mas melhora a segurança.</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Leia-me</string>\n    <string name=\"module_releases\">Versões</string>\n    <string name=\"module_information\">Informações</string>\n    <string name=\"module_information_homepage\">Site</string>\n    <string name=\"module_information_source_url\">Código-fonte</string>\n    <string name=\"module_information_collaborators\">Colaboradores</string>\n    <string name=\"module_release_view_assets\">Conteúdos</string>\n    <string name=\"menu_open_in_browser\">Abrir no navegador</string>\n    <string name=\"module_release_load_more\">Mostrar versões anteriores</string>\n    <string name=\"module_release_no_more\">Não há mais versões</string>\n    <string name=\"repo_load_failed\">Falha ao carregar o repositório do módulo %s</string>\n    <string name=\"sort_upgradable_first\">Atualizável primeiro</string>\n    <string name=\"installed\">Instalado</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d baixado</item>\n        <item quantity=\"other\">%d baixados</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Vermelho</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Roxo</string>\n    <string name=\"color_deep_purple\">Roxo profundo</string>\n    <string name=\"color_indigo\">Azul-anil</string>\n    <string name=\"color_blue\">Azul</string>\n    <string name=\"color_light_blue\">Azul claro</string>\n    <string name=\"color_cyan\">Ciano</string>\n    <string name=\"color_teal\">Verde-azulado</string>\n    <string name=\"color_green\">Verde</string>\n    <string name=\"color_light_green\">Verde claro</string>\n    <string name=\"color_lime\">Verde-limão</string>\n    <string name=\"color_yellow\">Amarelo</string>\n    <string name=\"color_amber\">Âmbar</string>\n    <string name=\"color_orange\">Laranja</string>\n    <string name=\"color_deep_orange\">Laranja profundo</string>\n    <string name=\"color_brown\">Marrom</string>\n    <string name=\"color_blue_grey\">Cinza-azulado</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ro/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Sumar</string>\n    <string name=\"Modules\">Module</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul activat</item>\n        <item quantity=\"few\">%d module activate</item>\n        <item quantity=\"other\">%d de module activate</item>\n    </plurals>\n    <string name=\"Logs\">Jurnale</string>\n    <string name=\"Settings\">Setări</string>\n    <string name=\"feedback_or_suggestion\">Feedback sau sugestii</string>\n    <string name=\"About\">Despre</string>\n    <string name=\"report_issue\">Semnalați o problemă</string>\n    <string name=\"module_repo\">Repozitoriu</string>\n    <string name=\"module_repo_up_to_date\">Modulele dv. sunt la zi</string>\n    <string name=\"module_repo_published_time\">Publicat în %s</string>\n    <string name=\"module_repo_updated_time\">Actualizat în %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul de actualizat</item>\n        <item quantity=\"few\">%d module de actualizat</item>\n        <item quantity=\"other\">%d de module de actualizat</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Vizualizați codul sursă pe %1$s<br/>Alăturați-vă grupului nostru %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Chele Ion, Igor Sorocean, br l12, Munteanu Roveliu, NicoaraAlex, Sergiu Alexa</string>\n    <string name=\"install\">Instalare</string>\n    <string name=\"install_summary\">Atingeți pentru a instala LSPosed</string>\n    <string name=\"not_installed\">Nu este instalat</string>\n    <string name=\"not_install_summary\">LSPosed nu este instalat</string>\n    <string name=\"activated\">Activat</string>\n    <string name=\"partial_activated\">Activat parțial</string>\n    <string name=\"selinux_policy_not_loaded_summary\">Politica SE nu este încărcată corect</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Unele funcții nu se pot activa.<br/>Vă rugăm să vă adresați dezvoltatorului <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Injectarea în Sistemul Android a eșuat</string>\n    <string name=\"system_inject_fail\"><![CDATA[Aceasta se întâmplă rar, iar cauza poate fi <b>Magisk</b> sau unele module de slabă calitate.<br/>Vă rugăm încercați să dezactivați modulele Magisk, exceptând Riru și LSPosed, sau să trimiteți dezvoltatorilor jurnalul complet.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Proprietăți de sistem invalide</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Unele proprietăți de sistem necesare pentru funcționarea LSPosed au fost eliminate sau modificate.<br/>Este posibil ca modulele să funcționeze intermitent.]]></string>\n    <string name=\"need_update\">Actualizare necesară</string>\n    <string name=\"please_update_summary\">Vă rugăm să actualizați LSPosed</string>\n    <string name=\"developer_warning_title\">Sfaturi pentru dezvoltatorii de module</string>\n    <string name=\"developer_warning_summary\">Vă rugăm să dezactivați optimizările automate oferite de Android Studio sau să executați `gradlew installDebug` pentru instalare. În caz contrar, aplicația nu va putea fi actualizată.</string>\n    <string name=\"info_api_version\">Versiune API</string>\n    <string name=\"info_framework_version\">Versiune platformă</string>\n    <string name=\"info_manager_package_name\">Numele pachetului manager</string>\n    <string name=\"info_system_version\">Versiune sistem</string>\n    <string name=\"info_device\">Dispozitiv</string>\n    <string name=\"info_system_abi\">Model de interfață binară</string>\n    <string name=\"info_dex2oat_wrapper\">Interfață pentru optimizarea compilării</string>\n    <string name=\"enabled\">Activat(ă)</string>\n    <string name=\"not_enabled\">Dezactivat(ă)</string>\n    <string name=\"supported\">Acceptat(ă)</string>\n    <string name=\"unsupported\">Neacceptat(ă)</string>\n    <string name=\"android_version_unsatisfied\">Versiune Android neacceptată</string>\n    <string name=\"crashed\">Oprit forțat</string>\n    <string name=\"mount_failed\">Montarea a eșuat</string>\n    <string name=\"selinux_permissive\">SELinux este facultativ</string>\n    <string name=\"sepolicy_incorrect\">Politica SELinux este incorectă</string>\n    <string name=\"update_lsposed\">Actualizați LSPosed</string>\n    <string name=\"update_lsposed_msg\">Confirmați actualizarea LSPosed? Dispozitivul va reporni după finalizarea actualizării.</string>\n    <string name=\"info_copied\">Copiat în clipboard</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Bine ați venit la LSPosed!</string>\n    <string name=\"parasitic_welcome_summary\">Folosiți managerul parazitar, care poate fi deschis dintr-o scurtătură creată de acesta sau din centrul de notificări.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Folosiți managerul parazitar, care poate fi deschis din centrul de notificări.</string>\n    <string name=\"create_shortcut\">Creează scurtătură</string>\n    <string name=\"never_show\">Nu afișați din nou</string>\n    <string name=\"app_welcome\">Managerul parazitar este recomandat</string>\n    <string name=\"app_welcome_summary\">LSPosed oferă posibilitatea parazitării sistemului, pentru a evita detectarea. Puteți deschide managerul parazitar din centrul de notificări. Se recomandă dezinstalarea aplicației curente.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Salvare</string>\n    <string name=\"nav_item_logs_verbose\">Jurnal detaliat</string>\n    <string name=\"nav_item_logs_module\">Jurnal module</string>\n    <string name=\"logs_saving\">Se salvează jurnalul, vă rugăm să așteptați</string>\n    <string name=\"logs_saved\">Jurnal salvat</string>\n    <string name=\"logs_save_failed2\">Salvarea a eșuat:\\n%s</string>\n    <string name=\"menuClearLog\">Golire jurnal</string>\n    <string name=\"logs_cleared\">Jurnalul a fost golit.</string>\n    <string name=\"scroll_top\">Derulare la început</string>\n    <string name=\"loading\">Se încarcă…</string>\n    <string name=\"scroll_bottom\">Derulare la final</string>\n    <string name=\"menuReload\">Reîncărcare</string>\n    <string name=\"logs_clear_failed_2\">Golirea jurnalului a eșuat</string>\n    <string name=\"menu_enable_word_wrap\">Încadrare cuvinte</string>\n    <string name=\"enabled_verbose_log\">Jurnal detaliat activat</string>\n    <string name=\"disabled_verbose_log\">Jurnal detaliat dezactivat</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(fără descriere)</string>\n    <string name=\"warning_xposed_min_version\">Acest modul nu poate fi activat, deoarece necesită o versiune Xposed mai nouă (%d).</string>\n    <string name=\"warning_target_version_higher\">Acest modul este proiectat pentru o versiune mai nouă a Xposed (%d). Este posibil ca unele funcții să nu poată fi activate.</string>\n    <string name=\"no_min_version_specified\">Acest modul nu specifică versiunea Xposed pentru care a fost proiectat.</string>\n    <string name=\"warning_min_version_too_low\">Acest modul a fost proiectat pentru versiunea Xposed %1$d, dar a fost dezactivat din cauza modificărilor incompatibile din versiunea %2$d.</string>\n    <string name=\"warning_installed_on_external_storage\">Acest modul nu poate fi încărcat, deoarece este instalat pe cardul SD. Vă rugăm să îl mutați pe stocarea internă.</string>\n    <string name=\"module_uninstall\">Dezinstalare</string>\n    <string name=\"module_settings\">Setări modul</string>\n    <string name=\"view_in_repo\">Vizualizați în repozitoriu</string>\n    <string name=\"module_uninstall_message\">Doriți să dezinstalați acest modul?</string>\n    <string name=\"module_uninstalled\">%1$s a fost dezinstalat</string>\n    <string name=\"module_uninstall_failed\">Dezinstalare nereușită</string>\n    <string name=\"add_module_to_user\">Adăugare modul pentru utilizator</string>\n    <string name=\"module_installed\">%1$s adăugat utilizatorului %2$s</string>\n    <string name=\"module_install_failed\">Adăugarea modulului a eșuat</string>\n    <string name=\"install_to_user\">Instalare modul pentru utilizatorul %s</string>\n    <string name=\"install_to_user_message\">Doriți să instalați %1$s pentru utilizatorul %2$s? Este recomandat să instalați manual, forțarea instalării prin LSPosed poate cauza probleme.</string>\n    <string name=\"expand\">extindere</string>\n    <string name=\"collapse\">restrângere</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Re-compilare</string>\n    <string name=\"compile_speed_msg\">Se compilează…</string>\n    <string name=\"compile_done\">Compilare finalizată</string>\n    <string name=\"app_launch\">Lansare</string>\n    <string name=\"compile_failed\">Compilare eșuată: valoarea returnată este goală</string>\n    <string name=\"compile_failed_with_info\">Compilare eșuată: </string>\n    <string name=\"sort_by_name\">Numele aplicației</string>\n    <string name=\"sort_by_package_name\">Numele pachetului</string>\n    <string name=\"sort_by_install_time\">Data instalării</string>\n    <string name=\"sort_by_update_time\">Data actualizării</string>\n    <string name=\"sort_reverse\">Ordine inversă</string>\n    <string name=\"menu_show_system_apps\">Aplicații de sistem</string>\n    <string name=\"menu_sort\">Sortare după</string>\n    <string name=\"enable_module\">Activare modul</string>\n    <string name=\"no_scope_selected\">Nu ați selectat nicio aplicație. Continuați?</string>\n    <string name=\"menu_show_games\">Jocuri</string>\n    <string name=\"menu_show_modules\">Module</string>\n    <string name=\"menu_show_denylist\">Aplicații din lista neagră</string>\n    <string name=\"failed_to_save_scope_list\">Actualizarea configurației a eșuat</string>\n    <string name=\"app_version\">Versiune: %1$s</string>\n    <string name=\"menu_select\">Selectați</string>\n    <string name=\"use_recommended\">Recomandate</string>\n    <string name=\"no_scope_selected_has_recommended\">Nu ați selectat nicio aplicație. Selectați aplicațiile recomandate?</string>\n    <string name=\"use_recommended_message\">Selectați aplicațiile recomandate?</string>\n    <string name=\"menu_select_all\">Toate</string>\n    <string name=\"menu_select_none\">Nimic</string>\n    <string name=\"menu_auto_include\">Automat, la instalare</string>\n    <string name=\"module_is_not_activated_yet\">Modulul Xposed nu este încă activat</string>\n    <string name=\"requested_by_module\">Recomandate</string>\n    <string name=\"update_available\">Actualizare disponibilă: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Nu ați selectat nicio aplicație. Modulul %s a fost dezactivat.</string>\n    <string name=\"android_framework\">Sistemul Android</string>\n    <string name=\"menu_backup_and_restore\">Copie de rezervă</string>\n    <string name=\"menu_backup\">Realizare copie</string>\n    <string name=\"menu_restore\">Restaurare</string>\n    <string name=\"force_stop\">Opriți forțat</string>\n    <string name=\"force_stop_dlg_title\">Opriți forțat?</string>\n    <string name=\"force_stop_dlg_text\">Dacă forțați oprirea unei aplicații, aceasta se poate comporta necorespunzător.</string>\n    <string name=\"reboot_required\">Repornirea este necesară pentru ca modificarea să aibă efect</string>\n    <string name=\"reboot\">Repornire</string>\n    <string name=\"menu_hide\">Ascundeți</string>\n    <string name=\"deny_list\">%s este în lista neagră. S-ar putea să nu aibă efect.</string>\n    <string name=\"deny_list_info\">În lista neagră</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Vizualizați în altă aplicație</string>\n    <string name=\"module_app_info\">Informații aplicație</string>\n    <string name=\"list_empty\">¯\\\\_(ツ)_\\/¯\\nAici nu e nimic</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Comportament</string>\n    <string name=\"settings_disable_verbose_log\">Dezactivare jurnale detaliate</string>\n    <string name=\"settings_disable_verbose_log_summary\">Rapoartele de depanare necesită includerea de jurnale detaliate</string>\n    <string name=\"settings_enable_log_watchdog\">Proces de supervizare cu jurnal</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Monitorizează LSPosed. Activarea acestuia modifică proprietățile de sistem, care pot fi exploatate de către alte aplicații pentru detectarea drepturilor de superutilizator</string>\n    <string name=\"pure_black_dark_theme\">Temă întunecată</string>\n    <string name=\"pure_black_dark_theme_summary\">Utilizați negru pur pentru tema întunecată</string>\n    <string name=\"settings_group_theme\">Temă</string>\n    <string name=\"settings_backup_and_restore\">Copie de rezervă și restaurare</string>\n    <string name=\"settings_backup_summery\">Exportă lista modulelor și configurația lor.</string>\n    <string name=\"settings_restore_summery\">Restaurează lista modulelor și configurația lor.</string>\n    <string name=\"settings_backup\">Realizați o copie de rezervă</string>\n    <string name=\"settings_backup_failed2\">Copia de rezervă a eșuat:\\n%s</string>\n    <string name=\"enable_documentui\">Vă rugăm activați aplicația „Fișiere“</string>\n    <string name=\"settings_restore\">Restaurați o copie de rezervă</string>\n    <string name=\"settings_restore_failed2\">Restaurarea a eșuat:\\n%s</string>\n    <string name=\"group_network\">Rețea</string>\n    <string name=\"dns_over_http\">DNS prin HTTPS</string>\n    <string name=\"dns_over_http_summary\">Încearcă evitarea limitărilor DNS din unele țări</string>\n    <string name=\"theme_color\">Culoarea temei</string>\n    <string name=\"theme_color_system\">Culoarea temei de sistem</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Afișare forțată a aplicațiilor în lansator</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">După Android 10, aplicațiile sunt forțate să afișeze pictograme în lansator. Setarea dezactivează această funcție de sistem.</string>\n    <string name=\"settings_group_system\">Sistem</string>\n    <string name=\"settings_language\">Limbă</string>\n    <string name=\"settings_translation_contributors\">Traducători</string>\n    <string name=\"settings_translation\">Contribuiți la traducere</string>\n    <string name=\"settings_translation_summary\">Ajutați-ne să traducem %s în limba dv.</string>\n    <string name=\"settings_create_shortcut_summary\">Creează o scurtătură pentru managerul parazitar</string>\n    <string name=\"settings_shortcut_pinned_hint\">Scurtătură creată</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Lansatorul implicit nu acceptă scurtături</string>\n    <string name=\"settings_enable_status_notification\">Notificare de stare</string>\n    <string name=\"settings_enable_status_notification_summary\">Afișează o notificare pentru deschiderea managerului parazitar</string>\n    <string name=\"disable_status_notification_error\">Nu se poate dezactiva notificarea: nu există scurtătură</string>\n    <string name=\"settings_update_channel\">Canal de actualizare</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nightly</string>\n    <string name=\"settings_xposed_api_call_protection\">Protecția apelurilor Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Interzice modulelor utilizarea codului încărcat dinamic pentru a apela Xposed. Acest lucru poate cauza probleme unor module, dar crește securitatea</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">A se citi mai întâi</string>\n    <string name=\"module_releases\">Versiuni</string>\n    <string name=\"module_information\">Informații</string>\n    <string name=\"module_information_homepage\">Pagină principală</string>\n    <string name=\"module_information_source_url\">Cod sursă</string>\n    <string name=\"module_information_collaborators\">Colaboratori</string>\n    <string name=\"module_release_view_assets\">Fișiere</string>\n    <string name=\"menu_open_in_browser\">Deschide în browser</string>\n    <string name=\"module_release_load_more\">Versiuni mai vechi</string>\n    <string name=\"module_release_no_more\">Nu există alte versiuni</string>\n    <string name=\"repo_load_failed\">Încărcarea repozitoriului a eșuat: %s</string>\n    <string name=\"sort_upgradable_first\">Actualizările întâi</string>\n    <string name=\"installed\">Instalat</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d descărcare</item>\n        <item quantity=\"few\">%d descărcări</item>\n        <item quantity=\"other\">%d de descărcări</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Roz-cireș</string>\n    <string name=\"color_red\">Roșu</string>\n    <string name=\"color_pink\">Roz</string>\n    <string name=\"color_purple\">Violet</string>\n    <string name=\"color_deep_purple\">Violet-închis</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Albastru</string>\n    <string name=\"color_light_blue\">Albastru-deschis</string>\n    <string name=\"color_cyan\">Albastru-verzui</string>\n    <string name=\"color_teal\">Verde-albăstrui</string>\n    <string name=\"color_green\">Verde</string>\n    <string name=\"color_light_green\">Verde-deschis</string>\n    <string name=\"color_lime\">Verde-lămâie</string>\n    <string name=\"color_yellow\">Galben</string>\n    <string name=\"color_amber\">Chihlimbar</string>\n    <string name=\"color_orange\">Portocaliu</string>\n    <string name=\"color_deep_orange\">Portocaliu-închis</string>\n    <string name=\"color_brown\">Maro</string>\n    <string name=\"color_blue_grey\">Gri-albăstrui</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Обзор</string>\n    <string name=\"Modules\">Модули</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">Включен %d модуль</item>\n        <item quantity=\"few\">Включены %d модуля</item>\n        <item quantity=\"many\">Включено %d модулей</item>\n        <item quantity=\"other\">Включено %d модулей</item>\n    </plurals>\n    <string name=\"Logs\">Журналы</string>\n    <string name=\"Settings\">Настройки</string>\n    <string name=\"feedback_or_suggestion\">Обратная связь</string>\n    <string name=\"About\">О приложении</string>\n    <string name=\"report_issue\">Сообщить о проблеме</string>\n    <string name=\"module_repo\">Репозиторий</string>\n    <string name=\"module_repo_up_to_date\">Все модули в актуальном состоянии</string>\n    <string name=\"module_repo_published_time\">Опубликовано %s</string>\n    <string name=\"module_repo_updated_time\">Обновлено %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">Доступно обновление %d модуля</item>\n        <item quantity=\"few\">Доступны обновления %d модулей</item>\n        <item quantity=\"many\">Доступны обновления %d модулей</item>\n        <item quantity=\"other\">Доступны обновления %d модулей</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Посмотрите исходный код на %1$s<br/><br/>Заходите на наш %2$s-канал]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Alеxеy, ded Arkash</string>\n    <string name=\"install\">Установить</string>\n    <string name=\"install_summary\">Нажмите, чтобы установить LSPosed</string>\n    <string name=\"not_installed\">Не установлен</string>\n    <string name=\"not_install_summary\">LSPosed не установлен!</string>\n    <string name=\"activated\">Активирован</string>\n    <string name=\"partial_activated\">Частично активирован</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy загружен неправильно</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Некоторые функции не будут работать.<br/>Пожалуйста, сообщите об этом разработчикам <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Системный фреймворк (System Framework): ошибка внедрения</string>\n    <string name=\"system_inject_fail\"><![CDATA[Это редкость и может быть вызвано самим <b>Magisk</b> или его несовместимыми модулями.<br/>Попробуйте отключить модули Magisk, кроме Riru и LSPosed, или пришлите полный журнал разработчикам.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Неправильные системные настройки</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Некоторые необходимые системные настройки удалены или изменены.<br/>Модули могут не работать должным образом.]]></string>\n    <string name=\"need_update\">Требуется обновление</string>\n    <string name=\"please_update_summary\">Установите последнюю версию LSPosed!</string>\n    <string name=\"developer_warning_title\">Советы разработчику модулей</string>\n    <string name=\"developer_warning_summary\">Пожалуйста, отключите оптимизацию развёртывания в Android Studio или используйте команду `gradlew installDebug` для установки. В противном случае .apk-модуль не обновится.</string>\n    <string name=\"info_api_version\">Версия API</string>\n    <string name=\"info_framework_version\">Версия framework</string>\n    <string name=\"info_manager_package_name\">Имя пакета менеджера</string>\n    <string name=\"info_system_version\">Версия системы</string>\n    <string name=\"info_device\">Устройство</string>\n    <string name=\"info_system_abi\">Разрядность системы (ABI)</string>\n    <string name=\"info_dex2oat_wrapper\">Оптимизатор Dex</string>\n    <string name=\"enabled\">Вкл.</string>\n    <string name=\"not_enabled\">Выкл.</string>\n    <string name=\"supported\">Поддерживается</string>\n    <string name=\"unsupported\">Не поддерживается</string>\n    <string name=\"android_version_unsatisfied\">Версия Android не подходит</string>\n    <string name=\"crashed\">Сбой</string>\n    <string name=\"mount_failed\">Ошибка монтирования</string>\n    <string name=\"selinux_permissive\">SELinux в состоянии Permissive</string>\n    <string name=\"sepolicy_incorrect\">Неправильная политика SELinux</string>\n    <string name=\"update_lsposed\">Обновление LSPosed</string>\n    <string name=\"update_lsposed_msg\">Подтвердить обновление LSPosed? Устройство перезагрузится после завершения обновления</string>\n    <string name=\"info_copied\">Скопировано в буфер обмена</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Добро пожаловать в LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Вы используете parasitic-менеджер (т. е. скрытый режим): можно создать его ярлык на рабочем столе либо по-прежнему открывать через уведомление.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Вы используете parasitic-менеджер (т. е. скрытый режим), который можно открывать через уведомление.</string>\n    <string name=\"create_shortcut\">Создать ярлык</string>\n    <string name=\"never_show\">Больше не показывать</string>\n    <string name=\"app_welcome\">Рекомендуется parasitic-менеджер</string>\n    <string name=\"app_welcome_summary\">LSPosed теперь поддерживает «паразитирование», чтобы избежать обнаружение системой, — т. н. скрытый режим. Вы сможете открывать parasitic-менеджер LSPosed через уведомление. Текущее приложение рекомендуется удалить.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Сохранить</string>\n    <string name=\"nav_item_logs_verbose\">Подробный журнал</string>\n    <string name=\"nav_item_logs_module\">Журнал модулей</string>\n    <string name=\"logs_saving\">Журнал сохраняется, подождите</string>\n    <string name=\"logs_saved\">Журнал сохранён</string>\n    <string name=\"logs_save_failed2\">Ошибка сохранения:\\n%s</string>\n    <string name=\"menuClearLog\">Очистка журнала</string>\n    <string name=\"logs_cleared\">Журнал успешно очищен.</string>\n    <string name=\"scroll_top\">Промотать вверх</string>\n    <string name=\"loading\">Загрузка…</string>\n    <string name=\"scroll_bottom\">Промотать вниз</string>\n    <string name=\"menuReload\">Загрузить снова</string>\n    <string name=\"logs_clear_failed_2\">Ошибка очистки журнала</string>\n    <string name=\"menu_enable_word_wrap\">Перенос текста</string>\n    <string name=\"enabled_verbose_log\">Подробный журнал включен</string>\n    <string name=\"disabled_verbose_log\">Подробный журнал выключен</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(описание отсутствует)</string>\n    <string name=\"warning_xposed_min_version\">Данному модулю требуется более новая версия Xposed/LSPosed (%d), поэтому активация невозможна</string>\n    <string name=\"warning_target_version_higher\">Данный модуль разработан для более новой версии Xposed/LSPosed (%d), поэтому некоторые функции могут не работать</string>\n    <string name=\"no_min_version_specified\">В данном модуле не указана версия Xposed/LSPosed, необходимая для его работы.</string>\n    <string name=\"warning_min_version_too_low\">Данный модуль был создан для Xposed/LSPosed версии %1$d, но ввиду несовместимых изменений в версии %2$d он был выключен</string>\n    <string name=\"warning_installed_on_external_storage\">Данный модуль не может быть загружен, так как установлен на SD-карту. Переместите его во внутреннюю память</string>\n    <string name=\"module_uninstall\">Удалить</string>\n    <string name=\"module_settings\">Настройки модуля</string>\n    <string name=\"view_in_repo\">Смотреть в репо</string>\n    <string name=\"module_uninstall_message\">Вы точно хотите удалить данный модуль?</string>\n    <string name=\"module_uninstalled\">%1$s удалён</string>\n    <string name=\"module_uninstall_failed\">Удаление не удалось</string>\n    <string name=\"add_module_to_user\">Добавление модуля пользователю</string>\n    <string name=\"module_installed\">%1$s добавлен (пользователь %2$s)</string>\n    <string name=\"module_install_failed\">Добавление модуля не удалось</string>\n    <string name=\"install_to_user\">Установка (пользователь %s)</string>\n    <string name=\"install_to_user_message\">Вы точно хотите установить %1$s (пользователь %2$s)? Рекомендуется устанавливать вручную, принудительная установка через LSPosed может вызвать проблемы.</string>\n    <string name=\"expand\">развернуть</string>\n    <string name=\"collapse\">свернуть</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Повторная оптимизация</string>\n    <string name=\"compile_speed_msg\">Оптимизация…</string>\n    <string name=\"compile_done\">Оптимизация завершена!</string>\n    <string name=\"app_launch\">Запустить</string>\n    <string name=\"compile_failed\">Ошибка оптимизации: возвращено пустое значение!</string>\n    <string name=\"compile_failed_with_info\">Ошибка оптимизации: </string>\n    <string name=\"sort_by_name\">По названию приложения</string>\n    <string name=\"sort_by_package_name\">По названию пакета</string>\n    <string name=\"sort_by_install_time\">По времени установки</string>\n    <string name=\"sort_by_update_time\">По времени обновления</string>\n    <string name=\"sort_reverse\">Обратная сортировка</string>\n    <string name=\"menu_show_system_apps\">Системные приложения</string>\n    <string name=\"menu_sort\">Сортировка</string>\n    <string name=\"enable_module\">Включить модуль</string>\n    <string name=\"no_scope_selected\">Вы не выбрали ни одного приложения. Продолжить?</string>\n    <string name=\"menu_show_games\">Игры</string>\n    <string name=\"menu_show_modules\">Модули</string>\n    <string name=\"menu_show_denylist\">DenyList</string>\n    <string name=\"failed_to_save_scope_list\">Ошибка сохранения списка «охвата»</string>\n    <string name=\"app_version\">Версия: %1$s</string>\n    <string name=\"menu_select\">Выберите</string>\n    <string name=\"use_recommended\">Рекомендуемые</string>\n    <string name=\"no_scope_selected_has_recommended\">Вы не выбрали ни одного приложения. Выбрать рекомендуемые приложения?</string>\n    <string name=\"use_recommended_message\">Выбрать рекомендуемые приложения?</string>\n    <string name=\"menu_select_all\">Все</string>\n    <string name=\"menu_select_none\">Нет</string>\n    <string name=\"menu_auto_include\">Автовключение</string>\n    <string name=\"module_is_not_activated_yet\">Модуль Xposed пока не активирован</string>\n    <string name=\"requested_by_module\">Рекомендуемые</string>\n    <string name=\"update_available\">Доступно обновление: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Модуль %s выключен, так как не выбрано ни одно приложение.</string>\n    <string name=\"android_framework\">Системный фреймворк</string>\n    <string name=\"menu_backup_and_restore\">Резервная копия</string>\n    <string name=\"menu_backup\">Создать</string>\n    <string name=\"menu_restore\">Восстановить</string>\n    <string name=\"force_stop\">Остановить</string>\n    <string name=\"force_stop_dlg_title\">Остановить приложение?</string>\n    <string name=\"force_stop_dlg_text\">Принудительная остановка приложения может отразиться на его функциональности.</string>\n    <string name=\"reboot_required\">Для применения требуется перезагрузка</string>\n    <string name=\"reboot\">Перезагрузить</string>\n    <string name=\"menu_hide\">Скрыть</string>\n    <string name=\"deny_list\">%s находится в DenyList. Это может ни к чему не привести.</string>\n    <string name=\"deny_list_info\">В DenyList</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Посмотреть в другом приложении</string>\n    <string name=\"module_app_info\">О приложении</string>\n    <string name=\"list_empty\">¯\\\\_(ツ)_\\/¯\\nЗдесь ничего нет</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Выключить подробный журнал</string>\n    <string name=\"settings_disable_verbose_log_summary\">Отчёт об ошибке требует включение подробного журнала</string>\n    <string name=\"settings_enable_log_watchdog\">Включить отслеживание журнала</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Отслеживание журнала LSPosed (log watchdog) изменяет системные настройки, которыми можно обнаружить LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Чёрная тема</string>\n    <string name=\"pure_black_dark_theme_summary\">Использовать чёрную тему, когда включена тёмная тема</string>\n    <string name=\"settings_group_theme\">Тема</string>\n    <string name=\"settings_backup_and_restore\">Резервное копирование и восстановление</string>\n    <string name=\"settings_backup_summery\">Резервная копия списка модулей и списков «охвата».</string>\n    <string name=\"settings_restore_summery\">Восстановление списка модулей и списков «охвата».</string>\n    <string name=\"settings_backup\">Резервная копия</string>\n    <string name=\"settings_backup_failed2\">Ошибка создания резервной копии:\\n%s</string>\n    <string name=\"enable_documentui\">Пожалуйста, включите DocumentUI</string>\n    <string name=\"settings_restore\">Восстановить</string>\n    <string name=\"settings_restore_failed2\">Ошибка восстановления:\\n%s</string>\n    <string name=\"group_network\">Сеть</string>\n    <string name=\"dns_over_http\">DNS через HTTPS</string>\n    <string name=\"dns_over_http_summary\">Шифрование DNS-трафика для обхода ряда ограничений в сети</string>\n    <string name=\"theme_color\">Акцент темы</string>\n    <string name=\"theme_color_system\">Системный цвет акцента</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Принудительно показывать иконку модуля</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">После Android 10 приложениям запрещено скрывать иконку их лаунчера. Выключите переключатель, чтобы отключить эту системную функцию.</string>\n    <string name=\"settings_group_system\">Система</string>\n    <string name=\"settings_language\">Язык приложения</string>\n    <string name=\"settings_translation_contributors\">Авторы перевода</string>\n    <string name=\"settings_translation\">Принять участие в переводе</string>\n    <string name=\"settings_translation_summary\">Помогите нам перевести %s на ваш язык</string>\n    <string name=\"settings_create_shortcut_summary\">Создать ярлык, с помощью которого можно открыть parasitic-менеджер (т. е. LSPosed в скрытом режиме)</string>\n    <string name=\"settings_shortcut_pinned_hint\">Ярлык закреплён</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Текущий лаунчер по умолчанию не поддерживает закрепление ярлыков</string>\n    <string name=\"settings_enable_status_notification\">Уведомление о состоянии</string>\n    <string name=\"settings_enable_status_notification_summary\">Показывать уведомление, через которое можно открыть parasitic-менеджер (т. е. LSPosed в скрытом режиме)</string>\n    <string name=\"disable_status_notification_error\">Отсутствует ярлык, невозможно отключить уведомление</string>\n    <string name=\"settings_update_channel\">Канал обновлений</string>\n    <string name=\"update_channel_stable\">Стабильные версии</string>\n    <string name=\"update_channel_bate\">Бета-версии</string>\n    <string name=\"update_channel_nightly\">Еженочные версии</string>\n    <string name=\"settings_xposed_api_call_protection\">Защита запросов Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Блокировка динамически загружаемого кода модуля для использования Xposed API, это может нарушить работу некоторых модулей, но повысит безопасность</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">ПрочтиМеня</string>\n    <string name=\"module_releases\">Версии</string>\n    <string name=\"module_information\">Информация</string>\n    <string name=\"module_information_homepage\">Домашняя страница</string>\n    <string name=\"module_information_source_url\">Исходный код</string>\n    <string name=\"module_information_collaborators\">Разработчики</string>\n    <string name=\"module_release_view_assets\">Вложения</string>\n    <string name=\"menu_open_in_browser\">Открыть в браузере</string>\n    <string name=\"module_release_load_more\">Показать старые версии</string>\n    <string name=\"module_release_no_more\">Больше нет релизов</string>\n    <string name=\"repo_load_failed\">Ошибка загрузки репозитория модуля: %s</string>\n    <string name=\"sort_upgradable_first\">Сначала обновляемые</string>\n    <string name=\"installed\">Установлен</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d скачивание</item>\n        <item quantity=\"few\">%d скачивания</item>\n        <item quantity=\"many\">%d скачиваний</item>\n        <item quantity=\"other\">%d скачиваний</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Сакура</string>\n    <string name=\"color_red\">Красный</string>\n    <string name=\"color_pink\">Розовый</string>\n    <string name=\"color_purple\">Фиолетовый</string>\n    <string name=\"color_deep_purple\">Насыщенный фиолетовый</string>\n    <string name=\"color_indigo\">Индиго</string>\n    <string name=\"color_blue\">Синий</string>\n    <string name=\"color_light_blue\">Голубой</string>\n    <string name=\"color_cyan\">Циан</string>\n    <string name=\"color_teal\">Сине-зеленый</string>\n    <string name=\"color_green\">Зелёный</string>\n    <string name=\"color_light_green\">Светло-зелёный</string>\n    <string name=\"color_lime\">Лаймовый</string>\n    <string name=\"color_yellow\">Жёлтый</string>\n    <string name=\"color_amber\">Янтарный</string>\n    <string name=\"color_orange\">Оранжевый</string>\n    <string name=\"color_deep_orange\">Насыщенный оранжевый</string>\n    <string name=\"color_brown\">Коричневый</string>\n    <string name=\"color_blue_grey\">Серо-голубой</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-si/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">දළ විශ්ලේෂණය</string>\n    <string name=\"Modules\">මොඩියුල</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d මොඩියුලය සක්රිය කරන්න</item>\n        <item quantity=\"other\">%d මොඩියුල සබල කර ඇත</item>\n    </plurals>\n    <string name=\"Logs\">සටහන්</string>\n    <string name=\"Settings\">සැකසීම </string>\n    <string name=\"feedback_or_suggestion\">ෆිඩ්බැක් ඔ යෝජනා </string>\n    <string name=\"About\">මේ ගැන</string>\n    <string name=\"report_issue\">ගැටලුවක් රිපෝට් කර්න්න</string>\n    <string name=\"module_repo\">ගබඩාව</string>\n    <string name=\"module_repo_up_to_date\">සියලු මොඩියුල අප්ඩේට් ය</string>\n    <string name=\"module_repo_published_time\">%s හි ප්‍රකාශයට පත් කරන ලදී</string>\n    <string name=\"module_repo_updated_time\">%sට අප්ඩේට් කරන ලදී</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d මොඩියුලය උත්ශ්‍රේණි කළ හැකිය</item>\n        <item quantity=\"other\">%d මොඩියුල උත්ශ්‍රේණි කළ හැකිය </item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[මූල කේතය බලන්න අපේ නාලිකාවට %1$s<br/> සම්බන්ධ වන්න %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">ඉන්ට්ට්‍රොල්</string>\n    <string name=\"install_summary\">LSPosed ස්ථාපනය කිරීමට තට්ටු කරන්න</string>\n    <string name=\"not_installed\">ස්ථාපනය කර නැත</string>\n    <string name=\"not_install_summary\">LSPosed ස්ථාපනය කර නැත</string>\n    <string name=\"activated\">සක්රිය කර ඇත</string>\n    <string name=\"partial_activated\">බාගෙට ඇක්ටිව් කර ඇත</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy හරියට ලෝඩ් වෙන් නෑ</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[සමහර විශේෂාංග ක්‍රියා නොකරනු ඇත.<br/>කරුණාකර මෙය <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> ඩිවලෝපර් වෙත රිපෝට් කරන්න.]]></string>\n    <string name=\"system_inject_fail_summary\">පද්ධති ෆ්‍රෙම්වවර් ඉන්ජෙක්ෂන් ෆෙල්ඩ්</string>\n    <string name=\"system_inject_fail\"><![CDATA[මෙය කලාතුරකින් වන අතර <b>Magisk</b> හෝ සමහර අඩු ගුණාත්මක Magisk මොඩියුල නිසා ඇති විය හැක.<br/>කරුණාකර Riru සහ LSPosed හැර අනෙකුත් Magisk මොඩියුල අක්‍රිය කිර උත්සාහ කරන්න හෝ ඩිවලොපර්ස්ලට සම්පූර්ණ ලොගය ඉදිරිපත් කරන්න.]]></string>\n    <string name=\"system_prop_incorrect_summary\">පද්ධති ප්‍රෝප් වැරදියි</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[සමහර අවශ්‍ය පද්ධති ගුණාංග මකා හෝ වෙනස් කරන ඇත.<br/>මොඩියුල වරින් වර අවලංගු විය හැක.]]></string>\n    <string name=\"need_update\">අප්ඩේට් කිරීම අවශ්‍ය වේ</string>\n    <string name=\"please_update_summary\">කරුණාකර LSPposed හි නවතම අනුවාදය ස්ථාපනය කරන්න</string>\n    <string name=\"info_api_version\">API අනුවාදය</string>\n    <string name=\"info_framework_version\">ෆ්‍රෙම්වර්ක් අනුවාදය</string>\n    <string name=\"info_manager_package_name\">කළමනාකරු පැකේජයේ නම</string>\n    <string name=\"info_system_version\">පද්ධති අනුවාදය</string>\n    <string name=\"info_device\">උපාංගය</string>\n    <string name=\"info_system_abi\">පද්ධති ABI</string>\n    <string name=\"info_dex2oat_wrapper\">ඩෙක්ස් ඔප්ටිමයිසේර් ව්‍රප්පර්</string>\n    <string name=\"enabled\">සක්‍රීය කර ඇත</string>\n    <string name=\"not_enabled\">සක්‍රීය කර නැත</string>\n    <string name=\"supported\">සහාය දුන්නා</string>\n    <string name=\"unsupported\">සහාය නොදක්වයි</string>\n    <string name=\"android_version_unsatisfied\">Android අනුවාදය තෘප්තිමත් නොවේ</string>\n    <string name=\"crashed\">කඩා වැටුණා</string>\n    <string name=\"mount_failed\">සවි කිරීම අසාර්ථක විය</string>\n    <string name=\"selinux_permissive\">SELinux අවසර ඇත</string>\n    <string name=\"sepolicy_incorrect\">SELinux ප්‍රතිපත්තිය වැරදියි</string>\n    <string name=\"update_lsposed\">LSPposed යාවත්කාලීන කරන්න</string>\n    <string name=\"update_lsposed_msg\">LSPposed යාවත්කාලීන කිරීම තහවුරු කරන්නද? යාවත්කාලීන කිරීම අවසන් වූ පසු මෙම උපාංගය නැවත ආරම්භ වේ</string>\n    <string name=\"info_copied\">පසුරු පුවරුවට පිටපත් කර ඇත</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPposed වෙත සාදරයෙන් පිළිගනිමු</string>\n    <string name=\"parasitic_welcome_summary\">ඔබ කෙටිමඟ නිර්මාණය කළ හැකි හෝ තවමත් දැනුම්දීමෙන් විවෘත කළ හැකි පරපෝෂිත කළමනාකරු භාවිත කරයි.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">දැනුම්දීමකින් විවෘත කළ හැකි පරපෝෂිත කළමනාකරු ඔබ භාවිත කරයි.</string>\n    <string name=\"create_shortcut\">කෙටිමං සාදන්න</string>\n    <string name=\"never_show\">කවදාවත් පෙන්වන්න එපා</string>\n    <string name=\"app_welcome\">පරපෝෂිත කළමනාකරු නිර්දේශ කෙරේ</string>\n    <string name=\"app_welcome_summary\">LSPosed දැන් හඳුනාගැනීම වැළැක්වීම සඳහා පද්ධති පරපෝෂිතකරණයට සහය දක්වයි, ඔබට දැනුම්දීමෙන් පරපෝෂිත කළමනාකරු විවෘත කළ හැක. වත්මන් යෙදුම අස්ථාපනය කිරීම නිර්දේශ කෙරේ.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">සුරකින්න</string>\n    <string name=\"nav_item_logs_verbose\">වාචික ලඝු-සටහන්</string>\n    <string name=\"nav_item_logs_module\">මොඩියුල ලොග්</string>\n    <string name=\"logs_saving\">ලොගය සුරකිමින්, කරුණාකර රැඳී සිටින්න</string>\n    <string name=\"logs_saved\">ලඝු-සටහන් සුරකින ලදී</string>\n    <string name=\"logs_save_failed2\">සුරැකීමට අසමත් විය:\\n%s</string>\n    <string name=\"menuClearLog\">දැන් ලොගය හිස් කරන්න</string>\n    <string name=\"logs_cleared\">ලොගය සාර්ථකව හිස් කරන ලදී.</string>\n    <string name=\"scroll_top\">ඉහළට අනුචලනය කරන්න</string>\n    <string name=\"loading\">…පූරණය වේ</string>\n    <string name=\"scroll_bottom\">පහළට අනුචලනය කරන්න</string>\n    <string name=\"menuReload\">නැවත පූරණය කරන්න</string>\n    <string name=\"logs_clear_failed_2\">ලොගය හිස් කිරීමට අසමත් විය</string>\n    <string name=\"menu_enable_word_wrap\">වචන එතුම</string>\n    <string name=\"enabled_verbose_log\">වාචික ලොගය සක්‍රීය කර ඇත</string>\n    <string name=\"disabled_verbose_log\">වාචික ලොගය අක්‍රීය කර ඇත</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(විස්තරයක් සපයා නැත)</string>\n    <string name=\"warning_xposed_min_version\">මෙම මොඩියුලයට නව Xposed අනුවාදයක් අවශ්‍ය වේ (%d) එබැවින් සක්‍රිය කළ නොහැක</string>\n    <string name=\"warning_target_version_higher\">මෙම මොඩියුලය නවතම Xposed අනුවාදයක් සඳහා නිර්මාණය කර ඇත (%d) එබැවින් සමහර ක්‍රියාකාරීත්වයන් ක්‍රියා නොකරනු ඇත</string>\n    <string name=\"no_min_version_specified\">මෙම මොඩියුලය එයට අවශ්‍ය Xposed අනුවාදය සඳහන් නොකරයි.</string>\n    <string name=\"warning_min_version_too_low\">මෙම මොඩියුලය Xposed අනුවාදය %1$dසඳහා නිර්මාණය කරන ලද නමුත් %2$dඅනුවාදයේ නොගැලපෙන වෙනස්කම් හේතුවෙන් එය අක්‍රීය කර ඇත</string>\n    <string name=\"warning_installed_on_external_storage\">මෙම මොඩියුලය SD පතෙහි ස්ථාපනය කර ඇති නිසා එය පූරණය කළ නොහැක, කරුණාකර එය අභ්‍යන්තර ආචයනය වෙත ගෙන යන්න</string>\n    <string name=\"module_uninstall\">අස්ථාපනය කරන්න</string>\n    <string name=\"module_settings\">මොඩියුල සැකසුම්</string>\n    <string name=\"view_in_repo\">Repo හි බලන්න</string>\n    <string name=\"module_uninstall_message\">ඔබට මෙම මොඩියුලය අස්ථාපනය කිරීමට අවශ්‍යද?</string>\n    <string name=\"module_uninstalled\">අස්ථාපනය %1$s</string>\n    <string name=\"module_uninstall_failed\">අස්ථාපනය අසාර්ථකයි</string>\n    <string name=\"add_module_to_user\">පරිශීලකයාට මොඩියුලය එක් කරන්න</string>\n    <string name=\"module_installed\">පරිශීලක %2$sවෙත %1$s එකතු කරන ලදී</string>\n    <string name=\"module_install_failed\">මොඩියුලය එකතු කිරීම අසාර්ථක විය</string>\n    <string name=\"install_to_user\">පරිශීලක %sවෙත ස්ථාපනය කරන්න</string>\n    <string name=\"install_to_user_message\">පරිශීලක %2$sවෙත %1$s ස්ථාපනය කිරීමට අවශ්‍යද? අතින් ස්ථාපනය කිරීම රෙකමදාරු කරනු ලැබේ, LSPosed හරහා බලහත්කාරයෙන් ස්ථාපනය කිරීම ගැටළු ඇති කළ හැකිය.</string>\n    <string name=\"expand\">පුළුල් කරන්න</string>\n    <string name=\"collapse\">කඩා වැටීම</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">නැවත ප්‍රශස්ත කරන්න</string>\n    <string name=\"compile_speed_msg\">ප්‍රශස්තකරණය…</string>\n    <string name=\"compile_done\">ප්‍රශස්තකරණය සම්පූර්ණයි</string>\n    <string name=\"app_launch\">එය දියත් කරන්න</string>\n    <string name=\"compile_failed\">ප්‍රශස්තකරණය අසාර්ථක විය: ආපසු ලැබෙන අගය හිස්ය</string>\n    <string name=\"compile_failed_with_info\">ප්‍රශස්තකරණය අසාර්ථක විය: </string>\n    <string name=\"sort_by_name\">අයදුම්පත් නම</string>\n    <string name=\"sort_by_package_name\">පැකේජයේ නම</string>\n    <string name=\"sort_by_install_time\">කාලය ස්ථාපනය කරන්න</string>\n    <string name=\"sort_by_update_time\">යාවත්කාලීන කාලය</string>\n    <string name=\"sort_reverse\">ආපසු හැරවීම</string>\n    <string name=\"menu_show_system_apps\">පද්ධති යෙදුම්</string>\n    <string name=\"menu_sort\">වර්ග කිරීම</string>\n    <string name=\"enable_module\">මොඩියුලය සක්රිය කරන්න</string>\n    <string name=\"no_scope_selected\">ඔබ කිසිදු යෙදුමක් තෝරාගෙන නැත. දිගටම කරගෙන යන්නද?</string>\n    <string name=\"menu_show_games\">ක්රීඩා</string>\n    <string name=\"menu_show_modules\">මොඩියුල</string>\n    <string name=\"menu_show_denylist\">ප්රතික්ෂේප කරන්නා</string>\n    <string name=\"failed_to_save_scope_list\">විෂය පථ ලැයිස්තුව සුරැකීමට අසමත් විය</string>\n    <string name=\"app_version\">අනුවාදය: %1$s</string>\n    <string name=\"use_recommended\">නිර්දේශ කර ඇත</string>\n    <string name=\"no_scope_selected_has_recommended\">ඔබ කිසිදු යෙදුමක් තෝරාගෙන නැත. නිර්දේශිත යෙදුම් තෝරන්නද?</string>\n    <string name=\"use_recommended_message\">නිර්දේශිත යෙදුම් තෝරන්නද?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed මොඩියුලය තවම සක්‍රිය කර නැත</string>\n    <string name=\"requested_by_module\">නිර්දේශ කර ඇත</string>\n    <string name=\"update_available\">යාවත්කාලීන ලබා ගත හැක: %1$s</string>\n    <string name=\"module_disabled_no_selection\">යෙදුමක් තෝරා නොගත් නිසා මොඩියුලය %s අබල කර ඇත.</string>\n    <string name=\"android_framework\">පද්ධති රාමුව</string>\n    <string name=\"menu_backup_and_restore\">උපස්ථ</string>\n    <string name=\"menu_backup\">උපස්ථ</string>\n    <string name=\"menu_restore\">ප්‍රතිෂ්ඨාපනය කරන්න</string>\n    <string name=\"force_stop\">බලෙන් නවත්වන්න</string>\n    <string name=\"force_stop_dlg_title\">බලෙන් නතර කරන්නද?</string>\n    <string name=\"force_stop_dlg_text\">ඔබ යෙදුමක් නැවැත්වීමට බල කළහොත්, එය වැරදි ලෙස හැසිරිය හැක.</string>\n    <string name=\"reboot_required\">මෙම වෙනස යෙදීම සඳහා නැවත පණගැන්වීම අවශ්‍ය වේ</string>\n    <string name=\"reboot\">නැවත ආරම්භ කරන්න</string>\n    <string name=\"menu_hide\">සඟවන්න</string>\n    <string name=\"deny_list\">%s denylist මත ඇත. එය ක්රියාත්මක නොවිය හැකිය.</string>\n    <string name=\"deny_list_info\">ප්‍රතික්ෂේප කරන්නා මත</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">වෙනත් යෙදුමක බලන්න</string>\n    <string name=\"module_app_info\">යෙදුම් තොරතුරු</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nමෙහි කිසිවක් නැත</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">රාමුව</string>\n    <string name=\"settings_disable_verbose_log\">වාචික ලඝු-සටහන් අක්‍රීය කරන්න</string>\n    <string name=\"settings_disable_verbose_log_summary\">වාචික ලඝු-සටහන් ඇතුළත් කිරීමට ගැටළු ඉල්ලීම් වාර්තා කරන්න</string>\n    <string name=\"pure_black_dark_theme\">කළු අඳුරු තේමාව</string>\n    <string name=\"pure_black_dark_theme_summary\">අඳුරු තේමාව සබල කර ඇත්නම් පිරිසිදු කළු තේමාව භාවිත කරන්න</string>\n    <string name=\"settings_group_theme\">තේමාව</string>\n    <string name=\"settings_backup_and_restore\">උපස්ථ සහ නැවත ස්ථාපිත</string>\n    <string name=\"settings_backup_summery\">උපස්ථ මොඩියුල ලැයිස්තුව සහ විෂය පථ ලැයිස්තු.</string>\n    <string name=\"settings_restore_summery\">මොඩියුල ලැයිස්තුව සහ විෂය පථ ලැයිස්තු ප්‍රතිසාධනය කරන්න.</string>\n    <string name=\"settings_backup\">උපස්ථ</string>\n    <string name=\"settings_backup_failed2\">උපස්ථ කිරීමට අසමත් විය:\\n%s</string>\n    <string name=\"enable_documentui\">කරුණාකර DocumentUI සබල කරන්න</string>\n    <string name=\"settings_restore\">ප්‍රතිෂ්ඨාපනය කරන්න</string>\n    <string name=\"settings_restore_failed2\">ප්‍රතිසාධනය කිරීමට අපොහොසත් විය:\\n%s</string>\n    <string name=\"group_network\">ජාල</string>\n    <string name=\"dns_over_http\">HTTPS හරහා DNS</string>\n    <string name=\"dns_over_http_summary\">සමහර රටවල ඩීඑන්එස් විෂ වීම සඳහා විසඳුම්</string>\n    <string name=\"theme_color\">තේමා වර්ණය</string>\n    <string name=\"theme_color_system\">පද්ධති තේමා වර්ණය</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">දියත් කිරීමේ අයිකන පෙන්වීමට යෙදුම් බල කරන්න</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10 ට පසුව, යෙදුම් වලට ඔවුන්ගේ දියත් කිරීමේ අයිකන සැඟවීමට ඉඩ නොදේ. මෙම පද්ධති විශේෂාංගය අක්‍රිය කිරීමට ටොගලය අක්‍රිය කරන්න.</string>\n    <string name=\"settings_group_system\">පද්ධති</string>\n    <string name=\"settings_language\">භාෂාව</string>\n    <string name=\"settings_translation_contributors\">පරිවර්තන දායකයින්</string>\n    <string name=\"settings_translation\">පරිවර්තනයට සහභාගී වන්න</string>\n    <string name=\"settings_translation_summary\">%s ඔබේ භාෂාවට පරිවර්තනය කිරීමට අපට උදවු කරන්න</string>\n    <string name=\"settings_create_shortcut_summary\">පරපෝෂිත කළමනාකරු විවෘත කළ හැකි කෙටිමඟක් සාදන්න</string>\n    <string name=\"settings_shortcut_pinned_hint\">කෙටිමඟ අමුණා ඇත</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">වත්මන් පෙරනිමි දියත් කිරීම pin කෙටිමං සඳහා සහය නොදක්වයි</string>\n    <string name=\"settings_enable_status_notification\">තත්ත්ව දැනුම්දීම</string>\n    <string name=\"settings_enable_status_notification_summary\">පරපෝෂිත කළමනාකරු විවෘත කළ හැකි දැනුම්දීමක් පෙන්වන්න</string>\n    <string name=\"disable_status_notification_error\">කෙටි මගක් නැත, දැනුම්දීම අක්‍රිය කළ නොහැක</string>\n    <string name=\"settings_update_channel\">නාලිකාව යාවත්කාලීන කරන්න</string>\n    <string name=\"update_channel_stable\">ස්ථාවර</string>\n    <string name=\"update_channel_bate\">බීටා</string>\n    <string name=\"update_channel_nightly\">රාත්රී ගොඩනැගීම</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API ඇමතුම් ආරක්ෂාව</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Xposed API භාවිතා කිරීමට ගතිකව පටවන ලද මොඩියුල කේතය අවහිර කරන්න, මෙය සමහර මොඩියුල බිඳ දැමිය හැකි නමුත් ආරක්ෂාවට ප්‍රතිලාභ ලබා දෙයි</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">කියවන්න</string>\n    <string name=\"module_releases\">නිකුත් කරයි</string>\n    <string name=\"module_information\">තොරතුරු</string>\n    <string name=\"module_information_homepage\">මුල් පිටුව</string>\n    <string name=\"module_information_source_url\">ප්රභව කේතය</string>\n    <string name=\"module_information_collaborators\">සහයෝගිතාකරුවන්</string>\n    <string name=\"module_release_view_assets\">වත්කම්</string>\n    <string name=\"menu_open_in_browser\">බ්‍රවුසරයේ විවෘත කරන්න</string>\n    <string name=\"module_release_load_more\">පැරණි අනුවාද පෙන්වන්න</string>\n    <string name=\"module_release_no_more\">තවත් නිදහස් කිරීමක් නැත</string>\n    <string name=\"repo_load_failed\">මොඩියුල repo: %sපූරණය කිරීමට අසමත් විය</string>\n    <string name=\"sort_upgradable_first\">පළමුව උසස් කළ හැකිය</string>\n    <string name=\"installed\">ස්ථාපනය කර ඇත</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d බාගැනීම</item>\n        <item quantity=\"other\">බාගැනීම් %d</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">සකුරා</string>\n    <string name=\"color_red\">රතු</string>\n    <string name=\"color_pink\">රෝස</string>\n    <string name=\"color_purple\">දම් පාට</string>\n    <string name=\"color_deep_purple\">තද දම්</string>\n    <string name=\"color_indigo\">ඉන්ඩිගෝ</string>\n    <string name=\"color_blue\">නිල්</string>\n    <string name=\"color_light_blue\">ලා නිල්</string>\n    <string name=\"color_cyan\">සියන්</string>\n    <string name=\"color_teal\">ටීල්</string>\n    <string name=\"color_green\">කොළ</string>\n    <string name=\"color_light_green\">ලා කොළ</string>\n    <string name=\"color_lime\">දෙහි</string>\n    <string name=\"color_yellow\">කහ</string>\n    <string name=\"color_amber\">ඇම්බර්</string>\n    <string name=\"color_orange\">දොඩම්</string>\n    <string name=\"color_deep_orange\">තද තැඹිලි</string>\n    <string name=\"color_brown\">දුඹුරු</string>\n    <string name=\"color_blue_grey\">නිල් අළු</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Prehľad</string>\n    <string name=\"Modules\">Moduly</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modul povolený</item>\n        <item quantity=\"few\">%d povolené moduly</item>\n        <item quantity=\"many\">%d povolené moduly</item>\n        <item quantity=\"other\">%d povolené moduly</item>\n    </plurals>\n    <string name=\"Logs\">Protokoly</string>\n    <string name=\"Settings\">Nastavenia</string>\n    <string name=\"feedback_or_suggestion\">Spätná väzba alebo návrh</string>\n    <string name=\"About\">O stránke</string>\n    <string name=\"report_issue\">Nahlásiť problém</string>\n    <string name=\"module_repo\">Úložisko</string>\n    <string name=\"module_repo_up_to_date\">Aktualizácia všetkých modulov</string>\n    <string name=\"module_repo_published_time\">Zverejnené na %s</string>\n    <string name=\"module_repo_updated_time\">Aktualizované na %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul s možnosťou aktualizácie</item>\n        <item quantity=\"few\">%d moduly s možnosťou aktualizácie</item>\n        <item quantity=\"many\">%d moduly s možnosťou aktualizácie</item>\n        <item quantity=\"other\">%d moduly s možnosťou aktualizácie</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Zobraziť zdrojový kód na %1$s<br/>Pripojte sa k nášmu kanálu %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">Inštalácia stránky</string>\n    <string name=\"install_summary\">Ťuknutím na položku nainštalujete LSPosed</string>\n    <string name=\"not_installed\">Nie je nainštalovaný</string>\n    <string name=\"not_install_summary\">LSPosed nie je nainštalovaný</string>\n    <string name=\"activated\">Aktivované</string>\n    <string name=\"partial_activated\">Čiastočne aktivované</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy sa nenačíta správne</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Niektoré funkcie nebudú fungovať.<br/>Nahláste to vývojárovi <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> .]]></string>\n    <string name=\"system_inject_fail_summary\">Vstreknutie systémového rámca zlyhalo</string>\n    <string name=\"system_inject_fail\"><![CDATA[Je to zriedkavé a môže to byť spôsobené <b>Magisk</b> alebo niektorými nekvalitnými modulmi Magisk.<br/>Skúste vypnúť iné moduly Magisk ako Riru a LSPosed alebo pošlite vývojárom celý log.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Nesprávna opora systému</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Niektoré potrebné vlastnosti systému boli odstránené alebo upravené.<br/>Moduly sa môžu občas znefunkčniť.]]></string>\n    <string name=\"need_update\">Je potrebné aktualizovať</string>\n    <string name=\"please_update_summary\">Nainštalujte si najnovšiu verziu aplikácie LSPosed</string>\n    <string name=\"info_api_version\">Verzia API</string>\n    <string name=\"info_framework_version\">Verzia rámca</string>\n    <string name=\"info_manager_package_name\">Názov balíka správcu</string>\n    <string name=\"info_system_version\">Verzia systému</string>\n    <string name=\"info_device\">Zariadenie</string>\n    <string name=\"info_system_abi\">Systém ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Povolené</string>\n    <string name=\"not_enabled\">Nie je povolené</string>\n    <string name=\"supported\">Podporované stránky</string>\n    <string name=\"unsupported\">Nepodporované</string>\n    <string name=\"android_version_unsatisfied\">Verzia pre Android nespokojná</string>\n    <string name=\"crashed\">Havarovaný</string>\n    <string name=\"mount_failed\">Montáž zlyhala</string>\n    <string name=\"selinux_permissive\">SELinux je permisívny</string>\n    <string name=\"sepolicy_incorrect\">Zásady systému SELinux sú nesprávne</string>\n    <string name=\"update_lsposed\">Aktualizácia LSPosed</string>\n    <string name=\"update_lsposed_msg\">Potvrdiť aktualizáciu LSPosed? Toto zariadenie sa po dokončení aktualizácie reštartuje</string>\n    <string name=\"info_copied\">Skopírované do schránky</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Vitajte v LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Používate parazitného správcu, ktorý môže vytvoriť zástupcu alebo ho stále otvárať z oznámenia.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Používate parazitného správcu, ktorý sa môže otvoriť z oznámenia.</string>\n    <string name=\"create_shortcut\">Vytvorenie skratky</string>\n    <string name=\"never_show\">Nikdy sa neukážte</string>\n    <string name=\"app_welcome\">Odporúčaný parazitický manažér</string>\n    <string name=\"app_welcome_summary\">LSPosed teraz podporuje parazitovanie systému, aby sa zabránilo detekcii, môžete otvoriť správcu parazitov z oznámenia. Odporúča sa odinštalovať aktuálnu aplikáciu.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Uložiť</string>\n    <string name=\"nav_item_logs_verbose\">Verbálne protokoly</string>\n    <string name=\"nav_item_logs_module\">Protokoly modulov</string>\n    <string name=\"logs_saving\">Ukladanie denníka, počkajte, prosím</string>\n    <string name=\"logs_saved\">Uložené protokoly</string>\n    <string name=\"logs_save_failed2\">Nepodarilo sa uložiť:\\n%s</string>\n    <string name=\"menuClearLog\">Vymazať protokol</string>\n    <string name=\"logs_cleared\">Protokol bol úspešne vymazaný.</string>\n    <string name=\"scroll_top\">Prejdite na začiatok</string>\n    <string name=\"loading\">Načítavanie…</string>\n    <string name=\"scroll_bottom\">Prejdite na spodok</string>\n    <string name=\"menuReload\">Znovunačítanie</string>\n    <string name=\"logs_clear_failed_2\">Nepodarilo sa vymazať protokol</string>\n    <string name=\"menu_enable_word_wrap\">Word Wrap</string>\n    <string name=\"enabled_verbose_log\">Verbose log povolený</string>\n    <string name=\"disabled_verbose_log\">Verbose log vypnuté</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(bez popisu)</string>\n    <string name=\"warning_xposed_min_version\">Tento modul vyžaduje novšiu verziu Xposed (%d), a preto ho nemožno aktivovať</string>\n    <string name=\"warning_target_version_higher\">Tento modul je určený pre novšiu verziu Xposed (%d), a preto niektoré funkcie nemusia fungovať</string>\n    <string name=\"no_min_version_specified\">Tento modul neuvádza verziu Xposed, ktorú potrebuje.</string>\n    <string name=\"warning_min_version_too_low\">Tento modul bol vytvorený pre verziu Xposed %1$d, ale kvôli nekompatibilným zmenám vo verzii %2$d, bol vypnutý.</string>\n    <string name=\"warning_installed_on_external_storage\">Tento modul nie je možné načítať, pretože je nainštalovaný na karte SD, prosím, presuňte ho do internej pamäte</string>\n    <string name=\"module_uninstall\">Odinštalovanie stránky</string>\n    <string name=\"module_settings\">Nastavenia modulu</string>\n    <string name=\"view_in_repo\">Zobraziť v časti Repo</string>\n    <string name=\"module_uninstall_message\">Chcete tento modul odinštalovať?</string>\n    <string name=\"module_uninstalled\">Odinštalovaná stránka %1$s</string>\n    <string name=\"module_uninstall_failed\">Odinštalovanie neúspešné</string>\n    <string name=\"add_module_to_user\">Pridanie modulu k používateľovi</string>\n    <string name=\"module_installed\">Pridanie stránky %1$s do používateľa %2$s</string>\n    <string name=\"module_install_failed\">Pridanie modulu zlyhalo</string>\n    <string name=\"install_to_user\">Inštalácia do používateľa %s</string>\n    <string name=\"install_to_user_message\">Chcete nainštalovať stránku %1$s na používateľa %2$s? Odporúča sa inštalovať ručne, vynútená inštalácia cez LSPosed môže spôsobiť problémy.</string>\n    <string name=\"expand\">rozšíriť</string>\n    <string name=\"collapse\">zrútenie</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Opätovná optimalizácia</string>\n    <string name=\"compile_speed_msg\">Optimalizácia…</string>\n    <string name=\"compile_done\">Optimalizácia dokončená</string>\n    <string name=\"app_launch\">Spustite ho</string>\n    <string name=\"compile_failed\">Optimalizácia zlyhala: návratová hodnota je prázdna</string>\n    <string name=\"compile_failed_with_info\">Optimalizácia zlyhala: </string>\n    <string name=\"sort_by_name\">Názov aplikácie</string>\n    <string name=\"sort_by_package_name\">Názov balíka</string>\n    <string name=\"sort_by_install_time\">Čas inštalácie</string>\n    <string name=\"sort_by_update_time\">Čas aktualizácie</string>\n    <string name=\"sort_reverse\">Reverzný</string>\n    <string name=\"menu_show_system_apps\">Systémové aplikácie</string>\n    <string name=\"menu_sort\">Triedenie</string>\n    <string name=\"enable_module\">Povolenie modulu</string>\n    <string name=\"no_scope_selected\">Nevybrali ste žiadnu aplikáciu. Pokračovať?</string>\n    <string name=\"menu_show_games\">Hry</string>\n    <string name=\"menu_show_modules\">Moduly</string>\n    <string name=\"menu_show_denylist\">Denylist</string>\n    <string name=\"failed_to_save_scope_list\">Nepodarilo sa uložiť zoznam oblastí</string>\n    <string name=\"app_version\">Verzia: %1$s</string>\n    <string name=\"use_recommended\">Odporúčané stránky</string>\n    <string name=\"no_scope_selected_has_recommended\">Nevybrali ste žiadnu aplikáciu. Vybrať odporúčané aplikácie?</string>\n    <string name=\"use_recommended_message\">Vybrať odporúčané aplikácie?</string>\n    <string name=\"module_is_not_activated_yet\">Modul Xposed ešte nie je aktivovaný</string>\n    <string name=\"requested_by_module\">Odporúčané stránky</string>\n    <string name=\"update_available\">Dostupná aktualizácia: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modul %s bol vypnutý, pretože nebola vybraná žiadna aplikácia.</string>\n    <string name=\"android_framework\">Systémový rámec</string>\n    <string name=\"menu_backup_and_restore\">Zálohovanie</string>\n    <string name=\"menu_backup\">Zálohovanie</string>\n    <string name=\"menu_restore\">Obnovenie</string>\n    <string name=\"force_stop\">Vynútiť zastavenie</string>\n    <string name=\"force_stop_dlg_title\">Zastaviť?</string>\n    <string name=\"force_stop_dlg_text\">Ak aplikáciu násilne zastavíte, môže sa správať nesprávne.</string>\n    <string name=\"reboot_required\">Na uplatnenie tejto zmeny je potrebný reštart</string>\n    <string name=\"reboot\">Reštart</string>\n    <string name=\"menu_hide\">Skryť</string>\n    <string name=\"deny_list\">%s je na denyliste. Nemusí nadobudnúť účinnosť.</string>\n    <string name=\"deny_list_info\">Na denyliste</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Zobraziť v inej aplikácii</string>\n    <string name=\"module_app_info\">Informácie o aplikácii</string>\n    <string name=\"list_empty\">\\\\\\\\_(ツ)_\\/¯\\nTu nič nie je</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Rámec</string>\n    <string name=\"settings_disable_verbose_log\">Zakázať slovné protokoly</string>\n    <string name=\"settings_disable_verbose_log_summary\">Žiadosť o nahlásenie problémov, aby obsahovala slovné protokoly</string>\n    <string name=\"pure_black_dark_theme\">Čierna tmavá téma</string>\n    <string name=\"pure_black_dark_theme_summary\">Použitie čisto čiernej témy, ak je povolená tmavá téma</string>\n    <string name=\"settings_group_theme\">Téma</string>\n    <string name=\"settings_backup_and_restore\">Zálohovanie a obnovenie</string>\n    <string name=\"settings_backup_summery\">Záložný zoznam modulov a zoznamy rozsahov.</string>\n    <string name=\"settings_restore_summery\">Obnovenie zoznamu modulov a zoznamov oblastí.</string>\n    <string name=\"settings_backup\">Zálohovanie</string>\n    <string name=\"settings_backup_failed2\">Nepodarilo sa zálohovať:\\n%s</string>\n    <string name=\"enable_documentui\">Povoľte DocumentUI</string>\n    <string name=\"settings_restore\">Obnovenie</string>\n    <string name=\"settings_restore_failed2\">Nepodarilo sa obnoviť:\\n%s</string>\n    <string name=\"group_network\">Sieť</string>\n    <string name=\"dns_over_http\">DNS cez HTTPS</string>\n    <string name=\"dns_over_http_summary\">Obchádzanie otravy DNS v niektorých krajinách</string>\n    <string name=\"theme_color\">Farba témy</string>\n    <string name=\"theme_color_system\">Farba témy systému</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Vynútenie aplikácií, aby zobrazovali ikony spúšťača</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Po systéme Android 10 aplikácie nemôžu skrývať ikony spúšťača. Vypnutím prepínača túto systémovú funkciu zakážete.</string>\n    <string name=\"settings_group_system\">Systém</string>\n    <string name=\"settings_language\">Jazyk</string>\n    <string name=\"settings_translation_contributors\">Spolupracovníci na preklade</string>\n    <string name=\"settings_translation\">Zúčastnite sa na preklade</string>\n    <string name=\"settings_translation_summary\">Pomôžte nám preložiť stránku %s do vášho jazyka</string>\n    <string name=\"settings_create_shortcut_summary\">Vytvorenie skratky, ktorá dokáže otvoriť parazitného správcu</string>\n    <string name=\"settings_shortcut_pinned_hint\">Skratka pripnutá</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Súčasný predvolený spúšťač nepodporuje pripnuté skratky</string>\n    <string name=\"settings_enable_status_notification\">Oznámenie o stave</string>\n    <string name=\"settings_enable_status_notification_summary\">Zobrazenie oznámenia, ktoré môže otvoriť parazitický správca</string>\n    <string name=\"disable_status_notification_error\">Žiadna skratka, nie je možné vypnúť upozornenie</string>\n    <string name=\"settings_update_channel\">Aktualizačný kanál</string>\n    <string name=\"update_channel_stable\">Stabilné</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nočné zostavenie</string>\n    <string name=\"settings_xposed_api_call_protection\">Ochrana volaní Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blokovať dynamicky načítaný kód modulu, aby používal Xposed API, čo môže rozbiť niektoré moduly, ale prospeje bezpečnosti</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Readme</string>\n    <string name=\"module_releases\">Uvoľňuje</string>\n    <string name=\"module_information\">Informácie</string>\n    <string name=\"module_information_homepage\">Domovská stránka</string>\n    <string name=\"module_information_source_url\">Zdrojový kód</string>\n    <string name=\"module_information_collaborators\">Spolupracovníci</string>\n    <string name=\"module_release_view_assets\">Aktíva</string>\n    <string name=\"menu_open_in_browser\">Otvoriť v prehliadači</string>\n    <string name=\"module_release_load_more\">Zobrazenie starších verzií</string>\n    <string name=\"module_release_no_more\">Žiadne ďalšie uvoľňovanie</string>\n    <string name=\"repo_load_failed\">Nepodarilo sa načítať modul repo: %s</string>\n    <string name=\"sort_upgradable_first\">Najskôr aktualizovateľné</string>\n    <string name=\"installed\">Nainštalované</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d stiahnuť</item>\n        <item quantity=\"few\">%d Na stiahnutie</item>\n        <item quantity=\"many\">%d Na stiahnutie</item>\n        <item quantity=\"other\">%d Na stiahnutie</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Červená</string>\n    <string name=\"color_pink\">Ružová</string>\n    <string name=\"color_purple\">Fialová</string>\n    <string name=\"color_deep_purple\">Tmavo fialová</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Modrá</string>\n    <string name=\"color_light_blue\">Svetlomodrá</string>\n    <string name=\"color_cyan\">Cyan</string>\n    <string name=\"color_teal\">Teal</string>\n    <string name=\"color_green\">Zelená</string>\n    <string name=\"color_light_green\">Svetlozelená</string>\n    <string name=\"color_lime\">Vápno</string>\n    <string name=\"color_yellow\">Žltá</string>\n    <string name=\"color_amber\">Amber</string>\n    <string name=\"color_orange\">Oranžová</string>\n    <string name=\"color_deep_orange\">Sýto oranžová</string>\n    <string name=\"color_brown\">Hnedá</string>\n    <string name=\"color_blue_grey\">Modrá šedá</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sv/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Översikt</string>\n    <string name=\"Modules\">Moduler</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d moduler aktiverade</item>\n        <item quantity=\"other\">%d moduler aktiverade</item>\n    </plurals>\n    <string name=\"Logs\">Loggar</string>\n    <string name=\"Settings\">Inställningar</string>\n    <string name=\"feedback_or_suggestion\">Återkoppling eller förslag</string>\n    <string name=\"About\">Om</string>\n    <string name=\"report_issue\">Rapportera problem</string>\n    <string name=\"module_repo\">Förvar</string>\n    <string name=\"module_repo_up_to_date\">Alla moduler uppdaterade</string>\n    <string name=\"module_repo_published_time\">Publicerad på %s</string>\n    <string name=\"module_repo_updated_time\">Uppdaterad på %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modul uppgraderbar</item>\n        <item quantity=\"other\">%d moduler uppgraderbara</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Visa källkod på %1$s<br/>Gå med i vår %2$s kanal]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Anton J</string>\n    <string name=\"install\">Installera</string>\n    <string name=\"install_summary\">Tryck för att installera LSPosed</string>\n    <string name=\"not_installed\">Inte installerad</string>\n    <string name=\"not_install_summary\">LSPosed är inte installerat</string>\n    <string name=\"activated\">Aktiverad</string>\n    <string name=\"partial_activated\">Delvis aktiverad</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy är inte korrekt laddat</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Vissa funktioner kommer inte att fungera.<br/>Vänligen rapportera detta till <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> utvecklaren.]]></string>\n    <string name=\"system_inject_fail_summary\">Injektion i System Framework misslyckades</string>\n    <string name=\"system_inject_fail\"><![CDATA[Detta är sällsynt och kan orsakas av <b>Magisk</b> eller några Magisk-moduler av låg kvalitet.<br/>Försök att inaktivera andra Magisk-moduler än Riru och LSPosed eller skicka fullständig logg till utvecklare.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Felaktig System prop</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Vissa nödvändiga systemegenskaper är raderade eller ändrade.<br/>Moduler kan komma att ogiltigförklara.]]></string>\n    <string name=\"need_update\">Behöver uppdateras</string>\n    <string name=\"please_update_summary\">Vänligen installera den senaste versionen av LSPosed</string>\n    <string name=\"developer_warning_title\">Tips för utvecklare av moduler</string>\n    <string name=\"developer_warning_summary\">Inaktivera optimeringar för distribution i Android Studio, eller använd kommandot `gradlew installDebug` för att installera. Annars kommer modulen apk inte att uppdateras.</string>\n    <string name=\"info_api_version\">API version</string>\n    <string name=\"info_framework_version\">Ramverk version</string>\n    <string name=\"info_manager_package_name\">Namn på managerpaket</string>\n    <string name=\"info_system_version\">Systemversion</string>\n    <string name=\"info_device\">Enhet</string>\n    <string name=\"info_system_abi\">System ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">Aktiverad</string>\n    <string name=\"not_enabled\">Inte aktiverad</string>\n    <string name=\"supported\">Stöds</string>\n    <string name=\"unsupported\">Ej stödd</string>\n    <string name=\"android_version_unsatisfied\">Android-version otillfredsställd</string>\n    <string name=\"crashed\">Kraschade</string>\n    <string name=\"mount_failed\">Montering misslyckades</string>\n    <string name=\"selinux_permissive\">SELinux är tillåtande</string>\n    <string name=\"sepolicy_incorrect\">SELinux-politiken är felaktig</string>\n    <string name=\"update_lsposed\">Uppdatera LSPosed</string>\n    <string name=\"update_lsposed_msg\">Bekräfta att uppdatera LSPosed? Denna enhet kommer att starta om efter att uppdateringen är slutförd</string>\n    <string name=\"info_copied\">Kopieras till urklipp</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Välkommen till LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Du använder Parasitic Manager, som kan skapa genvägar eller fortfarande öppna från anmälan.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Du använder Parasitic Manager, som kan öppnas från en avisering.</string>\n    <string name=\"create_shortcut\">Skapa genväg</string>\n    <string name=\"never_show\">Visa aldrig</string>\n    <string name=\"app_welcome\">Parasitic Manager Rekommenderad</string>\n    <string name=\"app_welcome_summary\">LSPosed stöder nu systemparasitering för att undvika upptäckt, du kan öppna parasithanteraren från anmälan. Det rekommenderas att avinstallera det aktuella programmet.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Spara</string>\n    <string name=\"nav_item_logs_verbose\">Utförliga Loggar</string>\n    <string name=\"nav_item_logs_module\">Modul-loggar</string>\n    <string name=\"logs_saving\">Sparar logg, vänligen vänta</string>\n    <string name=\"logs_saved\">Loggar som sparas</string>\n    <string name=\"logs_save_failed2\">Det gick inte att spara:\\n%s</string>\n    <string name=\"menuClearLog\">Rensa loggen nu</string>\n    <string name=\"logs_cleared\">Loggen har rensats.</string>\n    <string name=\"scroll_top\">Rulla till toppen</string>\n    <string name=\"loading\">Laddar…</string>\n    <string name=\"scroll_bottom\">Rulla till botten</string>\n    <string name=\"menuReload\">Reload</string>\n    <string name=\"logs_clear_failed_2\">Det gick inte att rensa loggen</string>\n    <string name=\"menu_enable_word_wrap\">Ord Wrap</string>\n    <string name=\"enabled_verbose_log\">Verbose logg aktiverad</string>\n    <string name=\"disabled_verbose_log\">Verbose logg inaktiverad</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(ingen beskrivning anges)</string>\n    <string name=\"warning_xposed_min_version\">Denna modul kräver en nyare Xposed version (%d) och kan därför inte aktiveras</string>\n    <string name=\"warning_target_version_higher\">Den här modulen är utformad för en nyare Xposed-version (%d) och därför kan det hända att vissa funktioner inte fungerar.</string>\n    <string name=\"no_min_version_specified\">Modulen anger inte den Xposed version som den behöver.</string>\n    <string name=\"warning_min_version_too_low\">Denna modul skapades för Xposed version %1$d, men på grund av inkompatibla förändringar i version %2$dhar den inaktiverats</string>\n    <string name=\"warning_installed_on_external_storage\">Modulen kan inte laddas eftersom den är installerad på SD-kortet, vänligen flytta den till internt lagringsutrymme</string>\n    <string name=\"module_uninstall\">Avinstallera</string>\n    <string name=\"module_settings\">Inställningar för modul</string>\n    <string name=\"view_in_repo\">Visa i Repo</string>\n    <string name=\"module_uninstall_message\">Vill du avinstallera denna modul?</string>\n    <string name=\"module_uninstalled\">Avinstallerade %1$s</string>\n    <string name=\"module_uninstall_failed\">Avinstallationen misslyckades</string>\n    <string name=\"add_module_to_user\">Lägg till modul till användare</string>\n    <string name=\"module_installed\">Lade till %1$s till användare %2$s</string>\n    <string name=\"module_install_failed\">Det gick inte att lägga till modul</string>\n    <string name=\"install_to_user\">Installera till användare %s</string>\n    <string name=\"install_to_user_message\">Vill du installera %1$s till användare %2$s? Det rekommenderas att installera manuellt, tvinga installation via LSPosed kan orsaka problem.</string>\n    <string name=\"expand\">expandera</string>\n    <string name=\"collapse\">kollapsa</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Återoptimera</string>\n    <string name=\"compile_speed_msg\">Optimerar…</string>\n    <string name=\"compile_done\">Optimering slutförd</string>\n    <string name=\"app_launch\">Starta den</string>\n    <string name=\"compile_failed\">Optimering misslyckades: returvärdet är tomt</string>\n    <string name=\"compile_failed_with_info\">Optimering misslyckades: </string>\n    <string name=\"sort_by_name\">Programmets namn</string>\n    <string name=\"sort_by_package_name\">Paketets namn</string>\n    <string name=\"sort_by_install_time\">Installera tid</string>\n    <string name=\"sort_by_update_time\">Uppdatera tid</string>\n    <string name=\"sort_reverse\">Omvänd</string>\n    <string name=\"menu_show_system_apps\">System appar</string>\n    <string name=\"menu_sort\">Sortering</string>\n    <string name=\"enable_module\">Aktivera modul</string>\n    <string name=\"no_scope_selected\">Du valde inte någon app. Fortsätt?</string>\n    <string name=\"menu_show_games\">Spel</string>\n    <string name=\"menu_show_modules\">Moduler</string>\n    <string name=\"menu_show_denylist\">Neka</string>\n    <string name=\"failed_to_save_scope_list\">Det gick inte att spara omfattningslistan</string>\n    <string name=\"app_version\">Version: %1$s</string>\n    <string name=\"menu_select\">Välj</string>\n    <string name=\"use_recommended\">Rekommenderad</string>\n    <string name=\"no_scope_selected_has_recommended\">Du valde inte någon app. Välj rekommenderade appar?</string>\n    <string name=\"use_recommended_message\">Välj rekommenderade appar?</string>\n    <string name=\"menu_select_all\">Alla</string>\n    <string name=\"menu_select_none\">Ingen</string>\n    <string name=\"menu_auto_include\">Automatisk inkludering</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modul är inte aktiverad än</string>\n    <string name=\"requested_by_module\">Rekommenderad</string>\n    <string name=\"update_available\">Uppdatering tillgänglig: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Modulen %s har inaktiverats sedan ingen app har valts.</string>\n    <string name=\"android_framework\">System ramverk</string>\n    <string name=\"menu_backup_and_restore\">Säkerhetskopiera</string>\n    <string name=\"menu_backup\">Säkerhetskopiera</string>\n    <string name=\"menu_restore\">Återställ</string>\n    <string name=\"force_stop\">Tvinga avslutad</string>\n    <string name=\"force_stop_dlg_title\">Tvinga avslut?</string>\n    <string name=\"force_stop_dlg_text\">Om du tvingar en app att sluta fungerar det inte.</string>\n    <string name=\"reboot_required\">Omstart krävs för att denna ändring ska gälla</string>\n    <string name=\"reboot\">Reboot</string>\n    <string name=\"menu_hide\">Dölj</string>\n    <string name=\"deny_list\">%s finns på förnekarlistan. Det kan inte börja gälla.</string>\n    <string name=\"deny_list_info\">På förnekelselista</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Visa i annan app</string>\n    <string name=\"module_app_info\">Information om appen</string>\n    <string name=\"list_empty\">&lt;unk&gt; \\\\\\\\_(&lt;unk&gt; )_\\/ &lt;unk&gt;\\nIngenting här</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Inaktivera utförliga loggar</string>\n    <string name=\"settings_disable_verbose_log_summary\">Begäran om att inkludera verbose-loggar i rapporten</string>\n    <string name=\"settings_enable_log_watchdog\">Aktivera loggövervakning</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Loggövervakning av LSPosed ändrar systemegenskaper, vilket kan utnyttjas för att upptäcka LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Svart mörkt tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Använd det rena svarta temat om mörkt tema är aktiverat</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Säkerhetskopiera och återställ</string>\n    <string name=\"settings_backup_summery\">Säkerhetskopiera modullista och omfattningslistor.</string>\n    <string name=\"settings_restore_summery\">Återställ modullista och omfattningslistor.</string>\n    <string name=\"settings_backup\">Säkerhetskopiera</string>\n    <string name=\"settings_backup_failed2\">Det gick inte att säkerhetskopiera:\\n%s</string>\n    <string name=\"enable_documentui\">Aktivera DocumentUI</string>\n    <string name=\"settings_restore\">Återställ</string>\n    <string name=\"settings_restore_failed2\">Det gick inte att återställa:\\n%s</string>\n    <string name=\"group_network\">Nätverk</string>\n    <string name=\"dns_over_http\">DNS över HTTPS</string>\n    <string name=\"dns_over_http_summary\">Workaround DNS-förgiftning i vissa nationer</string>\n    <string name=\"theme_color\">Tema färg</string>\n    <string name=\"theme_color_system\">Färg på systemtema</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Tvinga appar att visa launcher-ikoner</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Efter Android 10, appar är inte tillåtet att dölja sina launcher ikoner. Stäng av växla för att inaktivera den här systemfunktionen.</string>\n    <string name=\"settings_group_system\">System</string>\n    <string name=\"settings_language\">Språk</string>\n    <string name=\"settings_translation_contributors\">Bidragande översättare</string>\n    <string name=\"settings_translation\">Delta i översättning</string>\n    <string name=\"settings_translation_summary\">Hjälp oss att översätta %s till ditt språk</string>\n    <string name=\"settings_create_shortcut_summary\">Skapa en genväg som kan öppna Parasitic Manager</string>\n    <string name=\"settings_shortcut_pinned_hint\">Genväg fastlåst</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Den nuvarande standardstartaren har inte stöd för genvägar med stift</string>\n    <string name=\"settings_enable_status_notification\">Meddelande om status</string>\n    <string name=\"settings_enable_status_notification_summary\">Visa en anmälan som kan öppna parasitansvarig</string>\n    <string name=\"disable_status_notification_error\">Ingen genväg, kan inte inaktivera anmälan</string>\n    <string name=\"settings_update_channel\">Uppdatera kanal</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Nattligt bygg</string>\n    <string name=\"settings_xposed_api_call_protection\">Skydd av Xposed API-anrop</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Blockera dynamiskt laddad modulkod för att använda Xposed API, vilket kan förstöra vissa moduler men gynnar säkerheten.</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Läsa</string>\n    <string name=\"module_releases\">Släpp</string>\n    <string name=\"module_information\">Information</string>\n    <string name=\"module_information_homepage\">Hemsida</string>\n    <string name=\"module_information_source_url\">Källkod</string>\n    <string name=\"module_information_collaborators\">Medarbetare</string>\n    <string name=\"module_release_view_assets\">Tillgångar</string>\n    <string name=\"menu_open_in_browser\">Öppna i webbläsare</string>\n    <string name=\"module_release_load_more\">Visa äldre versioner</string>\n    <string name=\"module_release_no_more\">Ingen mer utgåva</string>\n    <string name=\"repo_load_failed\">Misslyckades att ladda modul rapo: %s</string>\n    <string name=\"sort_upgradable_first\">Uppgraderingsbar först</string>\n    <string name=\"installed\">Installerad</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d ladda ner</item>\n        <item quantity=\"other\">%d nedladdningar</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Sakura</string>\n    <string name=\"color_red\">Röd</string>\n    <string name=\"color_pink\">Rosa</string>\n    <string name=\"color_purple\">Lila</string>\n    <string name=\"color_deep_purple\">Djup lila</string>\n    <string name=\"color_indigo\">Indigo</string>\n    <string name=\"color_blue\">Blå</string>\n    <string name=\"color_light_blue\">Ljusblå</string>\n    <string name=\"color_cyan\">Turkos</string>\n    <string name=\"color_teal\">Kricka</string>\n    <string name=\"color_green\">Grön</string>\n    <string name=\"color_light_green\">Ljusgrön</string>\n    <string name=\"color_lime\">Limegrön</string>\n    <string name=\"color_yellow\">Gul</string>\n    <string name=\"color_amber\">Bärnsten</string>\n    <string name=\"color_orange\">Orange</string>\n    <string name=\"color_deep_orange\">Djup orange</string>\n    <string name=\"color_brown\">Brun</string>\n    <string name=\"color_blue_grey\">Blå grå</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sw600dp/integer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<resources>\n    <integer name=\"internal_fragment_bottom_insets\">0x50</integer> <!-- bottom -->\n</resources>\n\n"
  },
  {
    "path": "app/src/main/res/values-th/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">ภาพรวม</string>\n    <string name=\"Modules\">โมดูล</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">%d โมดูลที่เปิดใช้งาน</item>\n    </plurals>\n    <string name=\"Logs\">บันทึก</string>\n    <string name=\"Settings\">การตั้งค่า</string>\n    <string name=\"feedback_or_suggestion\">ข้อเสนอแนะ หรือ คำแนะนำ</string>\n    <string name=\"About\">เกี่ยวกับ</string>\n    <string name=\"report_issue\">รายงานปัญหา</string>\n    <string name=\"module_repo\">คลังโมดูล</string>\n    <string name=\"module_repo_up_to_date\">โมดูลทั้งหมดเป็นเวอร์ชั่นล่าสุด</string>\n    <string name=\"module_repo_published_time\">เผยแพร่เมื่อ %s</string>\n    <string name=\"module_repo_updated_time\">อัปเดตเมื่อ %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d โมดูลที่สามารถอัพเกรดได้</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[ดูซอร์สโค้ดได้ที่ %1$s<br/> หรือเข้าร่วม %2$s \n ของเรา]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">null</string>\n    <string name=\"install\">ติดตั้ง</string>\n    <string name=\"install_summary\">แตะเพื่อติดตั้ง LSPosed</string>\n    <string name=\"not_installed\">ไม่ได้ติดตั้ง</string>\n    <string name=\"not_install_summary\">ไม่ได้ติดตั้ง LSPosed</string>\n    <string name=\"activated\">เปิดใช้งานแล้ว</string>\n    <string name=\"partial_activated\">เปิดใช้งานบางส่วน</string>\n    <string name=\"selinux_policy_not_loaded_summary\">การโหลด SEPolicy ไม่ถูกต้อง</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[หากฟีเจอร์บางอย่างไม่ทำงาน<br/>โปรดรายงานที่<a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> developer.]]></string>\n    <string name=\"system_inject_fail_summary\">Injection ระบบ Framework ล้มเหลว</string>\n    <string name=\"system_inject_fail\"><![CDATA[สิ่งนี้เกิดขึ้นได้ยากและอาจเกิดจาก <b>Magisk</b> หรือโมดูล Magisk ที่มีคุณภาพต่ำบางตัว<br/>โปรดลองปิดการใช้งานโมดูล Magisk อื่นที่ไม่ใช่ Riru และ LSPosed หรือส่งบันทึกแบบเต็มไปยังนักพัฒนา]]></string>\n    <string name=\"system_prop_incorrect_summary\">System prop ไม่ถูกต้อง</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[คุณสมบัติระบบที่จำเป็นบางอย่างถูกลบหรือแก้ไข<br/>โมดูลอาจใช้ไม่ได้ในบางครั้ง]]></string>\n    <string name=\"need_update\">จำเป็นต้องอัพเดท</string>\n    <string name=\"please_update_summary\">โปรดติดตั้ง LSPosed . เวอร์ชันล่าสุด</string>\n    <string name=\"info_api_version\">เวอร์ชัน API</string>\n    <string name=\"info_framework_version\">เวอร์ชัน Framework</string>\n    <string name=\"info_manager_package_name\">ชื่อแพ็คเกจผู้จัดการ</string>\n    <string name=\"info_system_version\">เวอร์ชันของระบบ</string>\n    <string name=\"info_device\">อุปกรณ์</string>\n    <string name=\"info_system_abi\">ระบบ ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimizer Wrapper</string>\n    <string name=\"enabled\">เปิดใช้งาน</string>\n    <string name=\"not_enabled\">ไม่ได้เปิดใช้งาน</string>\n    <string name=\"supported\">ได้รับการสนับสนุน</string>\n    <string name=\"unsupported\">ไม่รองรับ</string>\n    <string name=\"android_version_unsatisfied\">เวอร์ชัน Android ไม่พอใจ</string>\n    <string name=\"crashed\">พัง</string>\n    <string name=\"mount_failed\">เมานต์ล้มเหลว</string>\n    <string name=\"selinux_permissive\">SELinux ได้รับอนุญาต</string>\n    <string name=\"sepolicy_incorrect\">นโยบาย SELinux ไม่ถูกต้อง</string>\n    <string name=\"update_lsposed\">อัพเดท LSPosed</string>\n    <string name=\"update_lsposed_msg\">ยืนยันการอัปเดต LSPosed? อุปกรณ์นี้จะรีบูตหลังจากอัปเดตเสร็จสิ้น</string>\n    <string name=\"info_copied\">คัดลอกไปยังคลิปบอร์ดแล้ว</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">ยินดีต้อนรับสู่ LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">คุณกำลังใช้ Lsposed ซึ่งสามารถสร้างทางลัดหรือยังคงเปิดจากการแจ้งเตือน</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">คุณกำลังใช้ตัวจัดการ Lsposed ซึ่งสามารถเปิดได้จากการแจ้งเตือน</string>\n    <string name=\"create_shortcut\">สร้างทางลัด</string>\n    <string name=\"never_show\">ไม่ต้องแสดง</string>\n    <string name=\"app_welcome\">แนะนำ กำจัดไวรัส</string>\n    <string name=\"app_welcome_summary\">LSPosed รองรับการปรสิตของระบบเพื่อหลีกเลี่ยงการตรวจจับ คุณสามารถเปิดโปรแกรมจัดการปรสิตได้จากการแจ้งเตือน ขอแนะนำให้ถอนการติดตั้งแอปพลิเคชันปัจจุบัน</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">บันทึก</string>\n    <string name=\"nav_item_logs_verbose\">บันทึกอย่างละเอียด</string>\n    <string name=\"nav_item_logs_module\">บันทึกโมดูล</string>\n    <string name=\"logs_saving\">กำลังบันทึกบันทึก โปรดรอสักครู่</string>\n    <string name=\"logs_saved\">บันทึก Log แล้ว</string>\n    <string name=\"logs_save_failed2\">ไม่สามารถบันทึก:\\n%s</string>\n    <string name=\"menuClearLog\">ล้างบันทึกตอนนี้</string>\n    <string name=\"logs_cleared\">ล้างบันทึกเรียบร้อยแล้ว</string>\n    <string name=\"scroll_top\">เลื่อนขึ้นด้านบน</string>\n    <string name=\"loading\">กำลังโหลด…</string>\n    <string name=\"scroll_bottom\">เลื่อนไปด้านล่าง</string>\n    <string name=\"menuReload\">โหลดซ้ำ</string>\n    <string name=\"logs_clear_failed_2\">ไม่สามารถล้างบันทึก</string>\n    <string name=\"menu_enable_word_wrap\">ตัดคำ</string>\n    <string name=\"enabled_verbose_log\">เปิดใช้งานบันทึกอย่างละเอียด</string>\n    <string name=\"disabled_verbose_log\">บันทึกรายละเอียดปิดการใช้งาน</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(ไม่มีคำอธิบายให้)</string>\n    <string name=\"warning_xposed_min_version\">โมดูลนี้ต้องการเวอร์ชัน Xposed ที่ใหม่กว่า (%d) ดังนั้นจึงไม่สามารถเปิดใช้งานได้</string>\n    <string name=\"warning_target_version_higher\">โมดูลนี้ออกแบบมาสำหรับ Xposed เวอร์ชั่น (%d) ดังนั้นฟังก์ชั่นบางอย่างอาจไม่ทำงาน</string>\n    <string name=\"no_min_version_specified\">โมดูลนี้ไม่ได้ระบุเวอร์ชัน Xposed ที่ต้องการ</string>\n    <string name=\"warning_min_version_too_low\">โมดูลนี้ถูกสร้างขึ้นสำหรับ Xposed เวอร์ชัน %1$dแต่เนื่องจากการเปลี่ยนแปลงที่เข้ากันไม่ได้ในเวอร์ชัน %2$dจึงถูกปิดใช้งาน</string>\n    <string name=\"warning_installed_on_external_storage\">ไม่สามารถโหลดโมดูลนี้ได้เนื่องจากติดตั้งอยู่ในการ์ด SD โปรดย้ายไปยังที่จัดเก็บข้อมูลภายใน</string>\n    <string name=\"module_uninstall\">ถอนการติดตั้ง</string>\n    <string name=\"module_settings\">การตั้งค่าโมดูล</string>\n    <string name=\"view_in_repo\">ดูใน Repo</string>\n    <string name=\"module_uninstall_message\">คุณต้องการถอนการติดตั้งโมดูลนี้หรือไม่?</string>\n    <string name=\"module_uninstalled\">ถอนการติดตั้ง %1$s</string>\n    <string name=\"module_uninstall_failed\">ถอนการติดตั้งไม่สำเร็จ</string>\n    <string name=\"add_module_to_user\">เพิ่มโมดูลให้กับผู้ใช้</string>\n    <string name=\"module_installed\">เพิ่ม %1$s ให้กับผู้ใช้ %2$s</string>\n    <string name=\"module_install_failed\">การเพิ่มโมดูลล้มเหลว</string>\n    <string name=\"install_to_user\">ติดตั้งให้กับผู้ใช้ %s</string>\n    <string name=\"install_to_user_message\">ต้องการติดตั้ง %1$s ให้กับผู้ใช้ %2$sหรือไม่ ขอแนะนำให้ติดตั้งด้วยตนเอง การบังคับติดตั้งผ่าน LSPosed อาจทำให้เกิดปัญหาได้</string>\n    <string name=\"expand\">ขยาย</string>\n    <string name=\"collapse\">ทรุด</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">เพิ่มประสิทธิภาพอีกครั้ง</string>\n    <string name=\"compile_speed_msg\">การเพิ่มประสิทธิภาพ…</string>\n    <string name=\"compile_done\">การเพิ่มประสิทธิภาพเสร็จสมบูรณ์</string>\n    <string name=\"app_launch\">เปิดตัวมัน</string>\n    <string name=\"compile_failed\">การเพิ่มประสิทธิภาพล้มเหลว: ค่าที่ส่งคืนว่างเปล่า</string>\n    <string name=\"compile_failed_with_info\">การเพิ่มประสิทธิภาพล้มเหลว: </string>\n    <string name=\"sort_by_name\">ชื่อแอปพลิเคชัน</string>\n    <string name=\"sort_by_package_name\">ชื่อแพ็คเกจ</string>\n    <string name=\"sort_by_install_time\">ติดตั้งเวลา</string>\n    <string name=\"sort_by_update_time\">อัพเดทเวลา</string>\n    <string name=\"sort_reverse\">ย้อนกลับ</string>\n    <string name=\"menu_show_system_apps\">แอประบบ</string>\n    <string name=\"menu_sort\">การเรียงลำดับ</string>\n    <string name=\"enable_module\">เปิดใช้งานโมดูล</string>\n    <string name=\"no_scope_selected\">คุณไม่ได้เลือกแอพใด ๆ ดำเนินการต่อ?</string>\n    <string name=\"menu_show_games\">เกม</string>\n    <string name=\"menu_show_modules\">โมดูล</string>\n    <string name=\"menu_show_denylist\">ปฏิเสธ</string>\n    <string name=\"failed_to_save_scope_list\">ไม่สามารถบันทึกรายการขอบเขต</string>\n    <string name=\"app_version\">เวอร์ชั่น: %1$s</string>\n    <string name=\"use_recommended\">ที่แนะนำ</string>\n    <string name=\"no_scope_selected_has_recommended\">คุณไม่ได้เลือกแอพใด ๆ เลือกแอพที่แนะนำ?</string>\n    <string name=\"use_recommended_message\">เลือกแอพที่แนะนำ?</string>\n    <string name=\"module_is_not_activated_yet\">โมดูล Xposed ยังไม่ได้เปิดใช้งาน</string>\n    <string name=\"requested_by_module\">ที่แนะนำ</string>\n    <string name=\"update_available\">มีการอัปเดต: %1$s</string>\n    <string name=\"module_disabled_no_selection\">โมดูล %s ถูกปิดใช้งานเนื่องจากไม่ได้เลือกแอป</string>\n    <string name=\"android_framework\">กรอบงานระบบ</string>\n    <string name=\"menu_backup_and_restore\">สำรอง</string>\n    <string name=\"menu_backup\">สำรอง</string>\n    <string name=\"menu_restore\">คืนค่า</string>\n    <string name=\"force_stop\">บังคับหยุด</string>\n    <string name=\"force_stop_dlg_title\">บังคับหยุด?</string>\n    <string name=\"force_stop_dlg_text\">หากคุณบังคับหยุดแอป แอปอาจทำงานผิดปกติ</string>\n    <string name=\"reboot_required\">จำเป็นต้องรีบูตเพื่อให้การเปลี่ยนแปลงนี้มีผล</string>\n    <string name=\"reboot\">รีบูต</string>\n    <string name=\"menu_hide\">ซ่อน</string>\n    <string name=\"deny_list\">%s อยู่ในรายการปฏิเสธ อาจไม่มีผล</string>\n    <string name=\"deny_list_info\">เกี่ยวกับผู้ปฏิเสธ</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">ดูในแอปอื่น</string>\n    <string name=\"module_app_info\">ข้อมูลแอพ</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nไม่มีอะไรที่นี่</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">กรอบ</string>\n    <string name=\"settings_disable_verbose_log\">ปิดใช้งานบันทึกอย่างละเอียด</string>\n    <string name=\"settings_disable_verbose_log_summary\">แจ้งปัญหาขอรวมบันทึกอย่างละเอียด</string>\n    <string name=\"pure_black_dark_theme\">ธีมสีดำมืด</string>\n    <string name=\"pure_black_dark_theme_summary\">ใช้ธีมสีดำล้วนหากเปิดใช้งานธีมสีเข้ม</string>\n    <string name=\"settings_group_theme\">ธีม</string>\n    <string name=\"settings_backup_and_restore\">สำรองและเรียกคืน</string>\n    <string name=\"settings_backup_summery\">รายการโมดูลสำรองและรายการขอบเขต</string>\n    <string name=\"settings_restore_summery\">คืนค่ารายการโมดูลและรายการขอบเขต</string>\n    <string name=\"settings_backup\">สำรอง</string>\n    <string name=\"settings_backup_failed2\">การสำรองข้อมูลล้มเหลว:\\n%s</string>\n    <string name=\"enable_documentui\">โปรดเปิดใช้งาน DocumentUI</string>\n    <string name=\"settings_restore\">คืนค่า</string>\n    <string name=\"settings_restore_failed2\">ไม่สามารถกู้คืนได้:\\n%s</string>\n    <string name=\"group_network\">เครือข่าย</string>\n    <string name=\"dns_over_http\">DNS ผ่าน HTTPS</string>\n    <string name=\"dns_over_http_summary\">วิธีแก้ปัญหา DNS เป็นขัดข้องในบางประเทศ</string>\n    <string name=\"theme_color\">สีของธีม</string>\n    <string name=\"theme_color_system\">สีของชุดรูปแบบของระบบ</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">บังคับให้แอปแสดงไอคอนตัวเรียกใช้</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">หลังจาก Android 10 แอปจะไม่ได้รับอนุญาตให้ซ่อนไอคอนตัวเรียกใช้งาน ปิดสวิตช์เพื่อปิดใช้งานคุณลักษณะของระบบนี้</string>\n    <string name=\"settings_group_system\">ระบบ</string>\n    <string name=\"settings_language\">ภาษา</string>\n    <string name=\"settings_translation_contributors\">ผู้ร่วมแปล</string>\n    <string name=\"settings_translation\">มีส่วนร่วมในการแปล</string>\n    <string name=\"settings_translation_summary\">ช่วยเราแปล %s เป็นภาษาของคุณ</string>\n    <string name=\"settings_create_shortcut_summary\">สร้างทางลัดที่สามารถเปิดตัวจัดการปรสิต</string>\n    <string name=\"settings_shortcut_pinned_hint\">ตรึงทางลัดแล้ว</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Launcher เริ่มต้นปัจจุบันไม่รองรับทางลัด PIN</string>\n    <string name=\"settings_enable_status_notification\">การแจ้งเตือนสถานะ</string>\n    <string name=\"settings_enable_status_notification_summary\">แสดงการแจ้งเตือนที่สามารถเปิดตัวจัดการปรสิต</string>\n    <string name=\"disable_status_notification_error\">ไม่มีทางลัด ไม่สามารถปิดการแจ้งเตือนได้</string>\n    <string name=\"settings_update_channel\">อัพเดทช่อง</string>\n    <string name=\"update_channel_stable\">มั่นคง</string>\n    <string name=\"update_channel_bate\">เบต้า</string>\n    <string name=\"update_channel_nightly\">สร้างทุกคืน</string>\n    <string name=\"settings_xposed_api_call_protection\">การป้องกันการโทร Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">บล็อกโค้ดโมดูลที่โหลดแบบไดนามิกเพื่อใช้ Xposed API ซึ่งอาจทำให้บางโมดูลเสียหายแต่ได้ประโยชน์ด้านความปลอดภัย</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">ต้องอ่าน</string>\n    <string name=\"module_releases\">เผยแพร่</string>\n    <string name=\"module_information\">ข้อมูล</string>\n    <string name=\"module_information_homepage\">หน้าแรก</string>\n    <string name=\"module_information_source_url\">รหัสแหล่งที่มา</string>\n    <string name=\"module_information_collaborators\">ผู้ร่วมงาน</string>\n    <string name=\"module_release_view_assets\">คลัง</string>\n    <string name=\"menu_open_in_browser\">เปิดในเบราว์เซอร์</string>\n    <string name=\"module_release_load_more\">แสดงเวอร์ชันเก่า</string>\n    <string name=\"module_release_no_more\">ไม่ปล่อยอีกแล้ว</string>\n    <string name=\"repo_load_failed\">ไม่สามารถโหลดโมดูล repo: %s</string>\n    <string name=\"sort_upgradable_first\">อัพเกรดก่อน</string>\n    <string name=\"installed\">ติดตั้งแล้ว</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d ดาวน์โหลด</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">ซากุระ</string>\n    <string name=\"color_red\">สีแดง</string>\n    <string name=\"color_pink\">สีชมพู</string>\n    <string name=\"color_purple\">สีม่วง</string>\n    <string name=\"color_deep_purple\">สีม่วงเข้ม</string>\n    <string name=\"color_indigo\">คราม</string>\n    <string name=\"color_blue\">สีน้ำเงิน</string>\n    <string name=\"color_light_blue\">ฟ้าอ่อน</string>\n    <string name=\"color_cyan\">สีฟ้า</string>\n    <string name=\"color_teal\">นกเป็ดน้ำ</string>\n    <string name=\"color_green\">สีเขียว</string>\n    <string name=\"color_light_green\">สีเขียวอ่อน</string>\n    <string name=\"color_lime\">มะนาว</string>\n    <string name=\"color_yellow\">สีเหลือง</string>\n    <string name=\"color_amber\">อำพัน</string>\n    <string name=\"color_orange\">ส้ม</string>\n    <string name=\"color_deep_orange\">ส้มเข้ม</string>\n    <string name=\"color_brown\">สีน้ำตาล</string>\n    <string name=\"color_blue_grey\">น้ำเงินเทา</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Genel bakış</string>\n    <string name=\"Modules\">Modüller</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d modül etkin</item>\n        <item quantity=\"other\">%d modüller etkin </item>\n    </plurals>\n    <string name=\"Logs\">Günlükler</string>\n    <string name=\"Settings\">Ayarlar</string>\n    <string name=\"feedback_or_suggestion\">Geri bildirim veya öneri</string>\n    <string name=\"About\">Hakkında</string>\n    <string name=\"report_issue\">Hata bildir</string>\n    <string name=\"module_repo\">Modül deposu</string>\n    <string name=\"module_repo_up_to_date\">Bütün modüller güncel</string>\n    <string name=\"module_repo_published_time\">%s yayınlandı</string>\n    <string name=\"module_repo_updated_time\">%s güncellendi</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d modülü güncellenebilir</item>\n        <item quantity=\"other\">%d modülleri güncellenebilir</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[%1$s adresinden kaynak kodu görüntüleyin<br/>%2$s kanalımıza katılın]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">Bedircan AYAYDIN, Furkan KARCIOĞLU, Ahmet AÇIKGÖZ, Sir-C</string>\n    <string name=\"install\">Yükle</string>\n    <string name=\"install_summary\">LSPosed\\'i yüklemek için dokunun</string>\n    <string name=\"not_installed\">Yüklenmedi</string>\n    <string name=\"not_install_summary\">LSPosed Kurulu değil</string>\n    <string name=\"activated\">Aktif edildi</string>\n    <string name=\"partial_activated\">Kısmen etkinleştirildi</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy düzgün yüklenmemiş</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Bazı özellikler çalışmayacaktır.<br/>Lütfen bunu <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> geliştiricisine bildirin.]]></string>\n    <string name=\"system_inject_fail_summary\">Sistem Çerçevesi enjeksiyonu başarısız oldu</string>\n    <string name=\"system_inject_fail\"><![CDATA[Bu nadiren yaşanır ve <b>Magisk</b> veya düşük kaliteli modüller yüzünden olabilir.<br/>Lütfen Riru ve LSPosed dışında bütün modülleri devre dışı bırakmayı deneyin veya geliştiricilere tam bir hata kaydı gönderin.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Sistem desteği yanlış</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Bazı gerekli sistem özellikleri silindi veya değiştirildi.<br/>Modüller zaman zaman düzgün çalışmayabilir.]]></string>\n    <string name=\"need_update\">Güncelleme gerekiyor</string>\n    <string name=\"please_update_summary\">Lütfen LSPosed\\'in en son sürümünü yükleyin</string>\n    <string name=\"developer_warning_title\">Modül geliştiricisi için ipuçları</string>\n    <string name=\"developer_warning_summary\">Lütfen Android Studio\\'da dağıtım optimizasyonlarını devre dışı bırakın veya yüklemek için `gradlew installDebug` komutunu kullanın. Aksi takdirde modül apk\\'sı güncellenmeyecektir.</string>\n    <string name=\"info_api_version\">API sürümü</string>\n    <string name=\"info_framework_version\">Çerçeve sürümü</string>\n    <string name=\"info_manager_package_name\">Yönetici paket adı</string>\n    <string name=\"info_system_version\">Sistem sürümü</string>\n    <string name=\"info_device\">Cihaz</string>\n    <string name=\"info_system_abi\">Sistem ABI\\'si</string>\n    <string name=\"info_dex2oat_wrapper\">Dex Optimize Edici Sarıcısı</string>\n    <string name=\"enabled\">Etkin</string>\n    <string name=\"not_enabled\">Etkin değil</string>\n    <string name=\"supported\">Desteklenen</string>\n    <string name=\"unsupported\">Desteklenmiyor</string>\n    <string name=\"android_version_unsatisfied\">Android sürümü tatmin edici değil</string>\n    <string name=\"crashed\">Çöktü</string>\n    <string name=\"mount_failed\">Bağlama başarısız</string>\n    <string name=\"selinux_permissive\">SELinux\\'a izin verildi</string>\n    <string name=\"sepolicy_incorrect\">SELinux politikası yanlış</string>\n    <string name=\"update_lsposed\">LSPosed\\'ı güncelle</string>\n    <string name=\"update_lsposed_msg\">LSPosed\\'ı güncellemek istediğinize emin misiniz? Bu cihaz güncelleme tamamlandıktan sonra yeniden başlatılacak</string>\n    <string name=\"info_copied\">Panoya kopyalandı</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPosed\\'a hoş geldiniz</string>\n    <string name=\"parasitic_welcome_summary\">Parazit yöneticiyi kullanıyorsunuz, bu da kısayol oluşturabileceğinizi veya hala bildirimden açabileceğinizi gösterir.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Parazit yöneticiyi kullanıyorsunuz ve bu da hala bildirimden açabileceğinizi gösterir.</string>\n    <string name=\"create_shortcut\">Kısayol oluştur</string>\n    <string name=\"never_show\">Asla gösterme</string>\n    <string name=\"app_welcome\">Parazit Yönetici Önerilir</string>\n    <string name=\"app_welcome_summary\">LSPosed tespit edilmeyi önlemek için sistem parazitlemeyi destekliyor, parazit yöneticiyi bildirimden açabilirsiniz. Şu anki uygulamayı kaldırmanız önerilir.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Kaydet</string>\n    <string name=\"nav_item_logs_verbose\">Ayrıntılı Günlükler</string>\n    <string name=\"nav_item_logs_module\">Modül Günlükleri</string>\n    <string name=\"logs_saving\">Günlük kaydediliyor, lütfen bekleyin</string>\n    <string name=\"logs_saved\">Günlükler kaydedildi</string>\n    <string name=\"logs_save_failed2\">Kaydedilemedi:\\n%s</string>\n    <string name=\"menuClearLog\">Günlüğü şimdi temizle</string>\n    <string name=\"logs_cleared\">Günlük başarıyla temizlendi.</string>\n    <string name=\"scroll_top\">Yukarı kaydır</string>\n    <string name=\"loading\">Yükleniyor…</string>\n    <string name=\"scroll_bottom\">Aşağı kaydır</string>\n    <string name=\"menuReload\">Tekrar yükle</string>\n    <string name=\"logs_clear_failed_2\">Günlük temizlenemedi</string>\n    <string name=\"menu_enable_word_wrap\">Yeni Satıra Geç</string>\n    <string name=\"enabled_verbose_log\">Ayrıntılı Günlükler Aktif</string>\n    <string name=\"disabled_verbose_log\">Ayrıntılı Günlükler Deaktif</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(açıklama yapılmadı)</string>\n    <string name=\"warning_xposed_min_version\">Bu modül daha yeni bir Xposed sürümü gerektiriyor (%d) ve bu nedenle etkinleştirilemez</string>\n    <string name=\"warning_target_version_higher\">Bu modül daha yeni bir Xposed sürümü (%d) için tasarlanmıştır ve bu nedenle bazı işlevler çalışmayabilir</string>\n    <string name=\"no_min_version_specified\">Bu modül, ihtiyaç duyduğu Xposed sürümünü belirtmiyor.</string>\n    <string name=\"warning_min_version_too_low\">Bu modül Xposed sürümü için oluşturuldu %1$d, ancak sürümdeki uyumsuz değişiklikler nedeniyle %2$d, devre dışı bırakıldı</string>\n    <string name=\"warning_installed_on_external_storage\">Bu modül yüklenemiyor çünkü SD kartta yüklü, lütfen dahili depolama birimine taşıyın veya SD kart erişimine izin verin</string>\n    <string name=\"module_uninstall\">Kaldır</string>\n    <string name=\"module_settings\">Modül ayarları</string>\n    <string name=\"view_in_repo\">Repo\\'da görüntüle</string>\n    <string name=\"module_uninstall_message\">Bu modülü kaldırmak istiyor musunuz?</string>\n    <string name=\"module_uninstalled\">%1$s kaldırıldı</string>\n    <string name=\"module_uninstall_failed\">Kaldırma başarısız</string>\n    <string name=\"add_module_to_user\">Kullanıcıya modül ekle</string>\n    <string name=\"module_installed\">Kullanıcıya %1$s eklendi %2$s</string>\n    <string name=\"module_install_failed\">Modül eklenemedi</string>\n    <string name=\"install_to_user\">Kullanıcıya yükle %s</string>\n    <string name=\"install_to_user_message\">%2$s kullanıcısına %1$s yüklemek ister misiniz? Elle kurulum yapılması tavsiye edilir, LSPosed ile kurulumu zorlamak sorunlara neden olabilir.</string>\n    <string name=\"expand\">genişlet</string>\n    <string name=\"collapse\">daralt</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Yeniden optimize et</string>\n    <string name=\"compile_speed_msg\">Optimize ediliyor…</string>\n    <string name=\"compile_done\">Optimizasyon tamamlandı</string>\n    <string name=\"app_launch\">Başlat</string>\n    <string name=\"compile_failed\">Optimizasyon başarısız oldu: dönüş değeri boş</string>\n    <string name=\"compile_failed_with_info\">Optimizasyon başarısız oldu: </string>\n    <string name=\"sort_by_name\">Uygulama Adı</string>\n    <string name=\"sort_by_package_name\">Paket ismi</string>\n    <string name=\"sort_by_install_time\">Yükleme zamanı</string>\n    <string name=\"sort_by_update_time\">Güncelleme zamanı</string>\n    <string name=\"sort_reverse\">Ters</string>\n    <string name=\"menu_show_system_apps\">Sistem uygulamaları</string>\n    <string name=\"menu_sort\">Sıralama</string>\n    <string name=\"enable_module\">Modülü etkinleştirin</string>\n    <string name=\"no_scope_selected\">Herhangi bir uygulama seçmediniz. Devam et?</string>\n    <string name=\"menu_show_games\">Oyunlar</string>\n    <string name=\"menu_show_modules\">Modüller</string>\n    <string name=\"menu_show_denylist\">Reddetme Listesi</string>\n    <string name=\"failed_to_save_scope_list\">Kapsam listesi kaydedilemedi</string>\n    <string name=\"app_version\">Sürüm: %1$s</string>\n    <string name=\"menu_select\">Seç</string>\n    <string name=\"use_recommended\">Önerilen</string>\n    <string name=\"no_scope_selected_has_recommended\">Herhangi bir uygulama seçmediniz. Önerilen uygulamalar seçilsin mi?</string>\n    <string name=\"use_recommended_message\">Önerilen uygulamalar seçilsin mi?</string>\n    <string name=\"menu_select_all\">Hepsi</string>\n    <string name=\"menu_select_none\">Hiçbiri</string>\n    <string name=\"menu_auto_include\">Otomatik Dahil Et</string>\n    <string name=\"module_is_not_activated_yet\">Xposed modülü henüz etkinleştirilmedi</string>\n    <string name=\"requested_by_module\">Önerilen</string>\n    <string name=\"update_available\">Güncelleme uygun: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Hiçbir uygulama seçilmediği için %s modülü devre dışı bırakıldı.</string>\n    <string name=\"android_framework\">Sistem Çerçevesi</string>\n    <string name=\"menu_backup_and_restore\">Yedekleme</string>\n    <string name=\"menu_backup\">Yedekleme</string>\n    <string name=\"menu_restore\">Geri yükle</string>\n    <string name=\"force_stop\">Durmaya zorla</string>\n    <string name=\"force_stop_dlg_title\">Zorla durdurulsun mu?</string>\n    <string name=\"force_stop_dlg_text\">Bir uygulamayı durdurmaya zorlarsanız, hatalı davranabilir.</string>\n    <string name=\"reboot_required\">Bu değişikliğin geçerli olması için yeniden başlatma gerekiyor</string>\n    <string name=\"reboot\">Yeniden başlat</string>\n    <string name=\"menu_hide\">Gizle</string>\n    <string name=\"deny_list\">%s reddetme listesinde olduğundan hiçbir etkisi olmayacak.</string>\n    <string name=\"deny_list_info\">Reddetme listesinde</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Diğer uygulamada görüntüle</string>\n    <string name=\"module_app_info\">Uygulama bilgisi</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nModül bulunamadı</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Çerçeve</string>\n    <string name=\"settings_disable_verbose_log\">Ayrıntılı günlükleri devre dışı bırak</string>\n    <string name=\"settings_disable_verbose_log_summary\">Ayrıntılı günlükleri dahil etme isteğini raporlayın</string>\n    <string name=\"settings_enable_log_watchdog\">Watchdog log\\'u etkinleştir</string>\n    <string name=\"settings_enable_log_watchdog_summary\">LSPosed\\'in watchdog log\\'u, LSPosed\\'i tespit etmek için kullanılabilecek sistem özelliklerini değiştirir</string>\n    <string name=\"pure_black_dark_theme\">Siyah koyu tema</string>\n    <string name=\"pure_black_dark_theme_summary\">Koyu tema etkinse saf siyah temayı kullanın</string>\n    <string name=\"settings_group_theme\">Tema</string>\n    <string name=\"settings_backup_and_restore\">Yedekle</string>\n    <string name=\"settings_backup_summery\">Modül ve kapsam listelerini yedekle.</string>\n    <string name=\"settings_restore_summery\">Modül ve kapsam listelerini geri yükle.</string>\n    <string name=\"settings_backup\">Yedek</string>\n    <string name=\"settings_backup_failed2\">Yedekleme başarısız oldu:\\n%s</string>\n    <string name=\"enable_documentui\">Lütfen DocumentUI\\'yi etkinleştirin</string>\n    <string name=\"settings_restore\">Geri yükle</string>\n    <string name=\"settings_restore_failed2\">Geri yüklenemedi:\\n%s</string>\n    <string name=\"group_network\">Ağ</string>\n    <string name=\"dns_over_http\">HTTPS üzerinden DNS</string>\n    <string name=\"dns_over_http_summary\">Bazı ülkelerde DNS zehirlenmesi için geçici çözümdür</string>\n    <string name=\"theme_color\">Tema rengi</string>\n    <string name=\"theme_color_system\">Sistem tema rengi</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Uygulamaları başlatıcı simgelerini göstermeye zorla</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10\\'dan sonra, uygulamaların başlatıcı simgelerini gizlenmesine izin verilmez. Bu sistem özelliğini devre dışı bırakmak için düğmeyi kapatın.</string>\n    <string name=\"settings_group_system\">Sistem</string>\n    <string name=\"settings_language\">Dil</string>\n    <string name=\"settings_translation_contributors\">Çeviri katılımcıları</string>\n    <string name=\"settings_translation\">Çeviriye katılın</string>\n    <string name=\"settings_translation_summary\">%s yazılımını çevirmemize yardım edin</string>\n    <string name=\"settings_create_shortcut_summary\">Parazit yöneticiyi açmak için bir kısayol oluşturun</string>\n    <string name=\"settings_shortcut_pinned_hint\">Ayarlar_kestirme_sabitlenmis</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Geçerli varsayılan başlatıcı, pin kısayollarını desteklemiyor</string>\n    <string name=\"settings_enable_status_notification\">Durum Bildirimi</string>\n    <string name=\"settings_enable_status_notification_summary\">Parazit yöneticiyi açabilmek için bir bildirim göster</string>\n    <string name=\"disable_status_notification_error\">Kısayol yok, bildirim devre dışı bırakılamaz</string>\n    <string name=\"settings_update_channel\">Güncelleme kanalı</string>\n    <string name=\"update_channel_stable\">Stabil</string>\n    <string name=\"update_channel_bate\">Beta</string>\n    <string name=\"update_channel_nightly\">Gecelik</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API çağrı koruması</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Xposed API kullanmak için dinamik olarak yüklenen modül kodunu engelleyin, bu bazı modülleri bozabilir ancak güvenliğe faydalı da olabilir</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Beni oku</string>\n    <string name=\"module_releases\">Sürümler</string>\n    <string name=\"module_information\">Bilgi</string>\n    <string name=\"module_information_homepage\">Ana Sayfa</string>\n    <string name=\"module_information_source_url\">Kaynak kodu</string>\n    <string name=\"module_information_collaborators\">Ortak çalışanlar</string>\n    <string name=\"module_release_view_assets\">Dosyalar</string>\n    <string name=\"menu_open_in_browser\">Tarayıcıda aç</string>\n    <string name=\"module_release_load_more\">Eski sürümleri göster</string>\n    <string name=\"module_release_no_more\">Daha fazla sürüm yok</string>\n    <string name=\"repo_load_failed\">Modül deposu yüklenemedi: %s</string>\n    <string name=\"sort_upgradable_first\">Önce yükseltilebilir</string>\n    <string name=\"installed\">Kurulmuş</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d İndir</item>\n        <item quantity=\"other\">%d indirmeler</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">sakura</string>\n    <string name=\"color_red\">Kırmızı</string>\n    <string name=\"color_pink\">Pembe</string>\n    <string name=\"color_purple\">Mor</string>\n    <string name=\"color_deep_purple\">Koyu mor</string>\n    <string name=\"color_indigo\">Çivit</string>\n    <string name=\"color_blue\">Mavi</string>\n    <string name=\"color_light_blue\">Açık mavi</string>\n    <string name=\"color_cyan\">Cam göbeği</string>\n    <string name=\"color_teal\">Turkuaz</string>\n    <string name=\"color_green\">Yeşil</string>\n    <string name=\"color_light_green\">Açık yeşil</string>\n    <string name=\"color_lime\">Limon</string>\n    <string name=\"color_yellow\">Sarı</string>\n    <string name=\"color_amber\">Kehribar</string>\n    <string name=\"color_orange\">Turuncu</string>\n    <string name=\"color_deep_orange\">Koyu turuncu</string>\n    <string name=\"color_brown\">Kahverengi</string>\n    <string name=\"color_blue_grey\">Mavi gri</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-uk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Огляд</string>\n    <string name=\"Modules\">Модулі</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d модуль увімкнено</item>\n        <item quantity=\"few\">%d модулі увімкнено</item>\n        <item quantity=\"many\">%d модулів увімкнено</item>\n        <item quantity=\"other\">%d модулів увімкнено</item>\n    </plurals>\n    <string name=\"Logs\">Звіти</string>\n    <string name=\"Settings\">Налаштування</string>\n    <string name=\"feedback_or_suggestion\">Зворотний зв\\'язок або пропозиція</string>\n    <string name=\"About\">Детальніше</string>\n    <string name=\"report_issue\">Повідомити про проблему</string>\n    <string name=\"module_repo\">Репозиторій</string>\n    <string name=\"module_repo_up_to_date\">Модулі не потребують оновлення</string>\n    <string name=\"module_repo_published_time\">Опубліковано о %s</string>\n    <string name=\"module_repo_updated_time\">Оновлено о %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d модуль потребує оновлення</item>\n        <item quantity=\"few\">%d модулі потребують оновлення</item>\n        <item quantity=\"many\">%d модулів потребують оновлення</item>\n        <item quantity=\"other\">%d модулів потребують оновлення</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Перегляньте вихідний код на %1$s<br/>Приєднуйтесь до нашого каналу в %2$s]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">AlexPechkin</string>\n    <string name=\"install\">Встановити</string>\n    <string name=\"install_summary\">Натисніть, щоб встановити LSPosed</string>\n    <string name=\"not_installed\">Не встановлено</string>\n    <string name=\"not_install_summary\">LSPosed не встановлено</string>\n    <string name=\"activated\">Активовано</string>\n    <string name=\"partial_activated\">Частково активовано</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy не завантажено належним чином</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Деякі функції не працюватимуть.<br/>Будь ласка, повідомте про це на <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> розробнику.]]></string>\n    <string name=\"system_inject_fail_summary\">Помилка ін\\'єкції System Framework</string>\n    <string name=\"system_inject_fail\"><![CDATA[Це рідкісна проблема, яка може бути спричинена <b>Magisk</b> або деякими неякісними модулями Magisk.<br/>Спробуйте вимкнути всі модулі Magisk, крім Riru та LSPosed, або надішліть повний звіт розробникам.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Неправильні системні налаштування</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Деякі необхідні параметри системи видалені або змінені.<br/>Модулі можуть не працювати належним чином.]]></string>\n    <string name=\"need_update\">Потрібно оновити</string>\n    <string name=\"please_update_summary\">Будь ласка, встановіть останню версію LSPosed</string>\n    <string name=\"developer_warning_title\">Поради розробнику модулів</string>\n    <string name=\"developer_warning_summary\">Будь ласка, відключіть оптимізацію розгортання в Android Studio або використовуйте команду `gradlew installDebug` для встановлення. В іншому випадку apk-модуль не оновиться.</string>\n    <string name=\"info_api_version\">Версія API</string>\n    <string name=\"info_framework_version\">Версія Framework</string>\n    <string name=\"info_manager_package_name\">Ім\\'я пакета менеджера</string>\n    <string name=\"info_system_version\">Версія Android</string>\n    <string name=\"info_device\">Пристрій</string>\n    <string name=\"info_system_abi\">Розрядність процесора</string>\n    <string name=\"info_dex2oat_wrapper\">Обгортка Dex Optimizer</string>\n    <string name=\"enabled\">Увімкнено</string>\n    <string name=\"not_enabled\">Вимкнено</string>\n    <string name=\"supported\">Підтримується</string>\n    <string name=\"unsupported\">Не підтримується</string>\n    <string name=\"android_version_unsatisfied\">Версія Android не підтримується</string>\n    <string name=\"crashed\">Помилка</string>\n    <string name=\"mount_failed\">Не вдалося під\\'єднати</string>\n    <string name=\"selinux_permissive\">SELinux є дозволеним</string>\n    <string name=\"sepolicy_incorrect\">Політика SELinux неправильна</string>\n    <string name=\"update_lsposed\">Оновити LSPosed</string>\n    <string name=\"update_lsposed_msg\">Підтвердити оновлення LSPosed? Цей пристрій перезавантажиться після завершення оновлення</string>\n    <string name=\"info_copied\">Скопійовано до буфера обміну</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Ласкаво просимо до LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">Ви використовуєте паразитний менеджер, який може створювати ярлик або все одно відкриватися з повідомлення.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Ви використовуєте паразитний менеджер, який може відкриватися з повідомлення.</string>\n    <string name=\"create_shortcut\">Створити ярлик</string>\n    <string name=\"never_show\">Ніколи не показувати</string>\n    <string name=\"app_welcome\">Рекомендується менеджер-паразит</string>\n    <string name=\"app_welcome_summary\">LSPosed тепер підтримує паразитування системи, щоб уникнути виявлення, ви можете відкрити диспетчер паразитів з повідомлення. Рекомендується видалити поточну програму.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Зберегти</string>\n    <string name=\"nav_item_logs_verbose\">Детальні звіти</string>\n    <string name=\"nav_item_logs_module\">Звіти модулів</string>\n    <string name=\"logs_saving\">Збереження журналу, зачекайте</string>\n    <string name=\"logs_saved\">Журнали збережено</string>\n    <string name=\"logs_save_failed2\">Не вдалося зберегти:\\n%s</string>\n    <string name=\"menuClearLog\">Очистити журнал зараз</string>\n    <string name=\"logs_cleared\">Журнал успішно очищено.</string>\n    <string name=\"scroll_top\">Прокрутити догори</string>\n    <string name=\"loading\">Завантаження…</string>\n    <string name=\"scroll_bottom\">Прокрутити донизу</string>\n    <string name=\"menuReload\">Перезавантажити</string>\n    <string name=\"logs_clear_failed_2\">Не вдалося очистити журнал</string>\n    <string name=\"menu_enable_word_wrap\">Перенесення слів</string>\n    <string name=\"enabled_verbose_log\">Детальний журнал увімкнено</string>\n    <string name=\"disabled_verbose_log\">Детальний журнал вимкнено</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(опису не надано)</string>\n    <string name=\"warning_xposed_min_version\">Цей модуль вимагає новішої версії Xposed (%d), тому його неможливо застосувати</string>\n    <string name=\"warning_target_version_higher\">Цей модуль розроблено для новішої версії Xposed (%d), тому деякі функції можуть не працювати</string>\n    <string name=\"no_min_version_specified\">У цьому модулі не вказано необхідну для нього версію Xposed.</string>\n    <string name=\"warning_min_version_too_low\">Цей модуль був створений для Xposed версії %1$d, але через несумісні зміни у версії %2$d його було вимкнено</string>\n    <string name=\"warning_installed_on_external_storage\">Цей модуль не може бути завантажений, оскільки він встановлений на SD-карті. Будь ласка, перенесіть його у внутрішню пам’ять</string>\n    <string name=\"module_uninstall\">Видалити</string>\n    <string name=\"module_settings\">Налаштування модуля</string>\n    <string name=\"view_in_repo\">Переглянути в репозиторії</string>\n    <string name=\"module_uninstall_message\">Ви впевнені, що хочете видалити цей модуль?</string>\n    <string name=\"module_uninstalled\">Видалено %1$s</string>\n    <string name=\"module_uninstall_failed\">Видалення не виконано</string>\n    <string name=\"add_module_to_user\">Додати модуль до користувача</string>\n    <string name=\"module_installed\">Додано %1$s до користувача %2$s</string>\n    <string name=\"module_install_failed\">Не вдалося додати модуль</string>\n    <string name=\"install_to_user\">Встановити користувачу %s</string>\n    <string name=\"install_to_user_message\">Бажаєте встановити %1$s користувачу %2$s? Рекомендується встановлювати вручну. Примусове встановлення за допомогою LSPosed може спричинити проблеми.</string>\n    <string name=\"expand\">розгорнути</string>\n    <string name=\"collapse\">згорнути</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Повторна оптимізація</string>\n    <string name=\"compile_speed_msg\">Оптимізація…</string>\n    <string name=\"compile_done\">Оптимізація завершена</string>\n    <string name=\"app_launch\">Запустити</string>\n    <string name=\"compile_failed\">Помилка оптимізації: повернуте значення порожнє</string>\n    <string name=\"compile_failed_with_info\">Помилка оптимізації: </string>\n    <string name=\"sort_by_name\">За назвою програми</string>\n    <string name=\"sort_by_package_name\">За іменем пакету</string>\n    <string name=\"sort_by_install_time\">За часом встановлення</string>\n    <string name=\"sort_by_update_time\">За часом оновлення</string>\n    <string name=\"sort_reverse\">У зворотньому порядку</string>\n    <string name=\"menu_show_system_apps\">Системні програми</string>\n    <string name=\"menu_sort\">Сортування</string>\n    <string name=\"enable_module\">Увімкнути модуль</string>\n    <string name=\"no_scope_selected\">Ви не вибрали жодної програми. Продовжити?</string>\n    <string name=\"menu_show_games\">Ігри</string>\n    <string name=\"menu_show_modules\">Модулі</string>\n    <string name=\"menu_show_denylist\">Чорний список</string>\n    <string name=\"failed_to_save_scope_list\">Не вдалося зберегти список областей</string>\n    <string name=\"app_version\">Версія: %1$s</string>\n    <string name=\"menu_select\">Виберіть</string>\n    <string name=\"use_recommended\">Рекомендовано</string>\n    <string name=\"no_scope_selected_has_recommended\">Ви не вибрали жодної програми. Вибрати рекомендовані програми?</string>\n    <string name=\"use_recommended_message\">Вибрати рекомендовані програми?</string>\n    <string name=\"menu_select_all\">Усі</string>\n    <string name=\"menu_select_none\">Немає</string>\n    <string name=\"menu_auto_include\">Автовключення</string>\n    <string name=\"module_is_not_activated_yet\">Модуль LSPosed ще не активований</string>\n    <string name=\"requested_by_module\">Рекомендовано</string>\n    <string name=\"update_available\">Доступне оновлення: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Модуль %s вимкнено, оскільки жодна програма не вибрана.</string>\n    <string name=\"android_framework\">System Framework</string>\n    <string name=\"menu_backup_and_restore\">Резервне копіювання</string>\n    <string name=\"menu_backup\">Створити резервну копію</string>\n    <string name=\"menu_restore\">Відновити з резервної копії</string>\n    <string name=\"force_stop\">Примусово зупинити</string>\n    <string name=\"force_stop_dlg_title\">Примусово зупинити?</string>\n    <string name=\"force_stop_dlg_text\">Примусове зупинення додатку, може мати наслідки.</string>\n    <string name=\"reboot_required\">Для застосування цієї зміни потрібне перезавантаження</string>\n    <string name=\"reboot\">Перезавантажити</string>\n    <string name=\"menu_hide\">Приховати</string>\n    <string name=\"deny_list\">%s в чорному списку. На цю програму не буде вплинуто.</string>\n    <string name=\"deny_list_info\">У чорному списку</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Переглянути в іншому додатку</string>\n    <string name=\"module_app_info\">Про програму</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nТут нічого немає</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Framework</string>\n    <string name=\"settings_disable_verbose_log\">Вимкнути детальні журнали</string>\n    <string name=\"settings_disable_verbose_log_summary\">Вимоги до звітів про проблеми, що виникають, включати розгорнуті журнали</string>\n    <string name=\"settings_enable_log_watchdog\">Увімкнути відстеження журналу</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Відстеження журналу LSPosed (log watchdog) змінює системні налаштування, якими можна виявити LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Чорна тема</string>\n    <string name=\"pure_black_dark_theme_summary\">Використовувати повністю чорну тему, коли темну тему увімкнено</string>\n    <string name=\"settings_group_theme\">Тема</string>\n    <string name=\"settings_backup_and_restore\">Резервне копіювання та відновлення</string>\n    <string name=\"settings_backup_summery\">Зберегти список увімкнених модулів та їхніх програм-жертв</string>\n    <string name=\"settings_restore_summery\">Відновити список увімкнених модулів та їхніх програм-жертв</string>\n    <string name=\"settings_backup\">Створити резервну копію</string>\n    <string name=\"settings_backup_failed2\">Не вдалося створити резервну копію:\\n%s</string>\n    <string name=\"enable_documentui\">Будь ласка, увімкніть DocumentUI</string>\n    <string name=\"settings_restore\">Відновити з резервної копії</string>\n    <string name=\"settings_restore_failed2\">Не вдалося зберегти:\\n%s</string>\n    <string name=\"group_network\">Мережа</string>\n    <string name=\"dns_over_http\">DNS поверх HTTPS</string>\n    <string name=\"dns_over_http_summary\">Обійти регіональне DNS-блокування</string>\n    <string name=\"theme_color\">Колір акценту</string>\n    <string name=\"theme_color_system\">Системний колір акценту</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Примусово показувати значки запуску</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Починаючи з Android 10, програмам не дозволяється приховувати свої значки запуску. Вимкніть перемикач, щоб вимкнути цю системну функцію.</string>\n    <string name=\"settings_group_system\">Система</string>\n    <string name=\"settings_language\">Мова</string>\n    <string name=\"settings_translation_contributors\">Автори перекладу</string>\n    <string name=\"settings_translation\">Допомогти з перекладом</string>\n    <string name=\"settings_translation_summary\">Доповнити український переклад %s</string>\n    <string name=\"settings_create_shortcut_summary\">Створіть ярлик, який зможе відкрити паразитний менеджер</string>\n    <string name=\"settings_shortcut_pinned_hint\">Ярлик закріплено</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Поточна панель запуску за замовчуванням не підтримує шорткати пін-кодів</string>\n    <string name=\"settings_enable_status_notification\">Повідомлення про статус</string>\n    <string name=\"settings_enable_status_notification_summary\">Показати повідомлення, яке може відкрити паразитний менеджер</string>\n    <string name=\"disable_status_notification_error\">Немає ярлика, не можна відключити сповіщення</string>\n    <string name=\"settings_update_channel\">Канал оновлень</string>\n    <string name=\"update_channel_stable\">Стабільний</string>\n    <string name=\"update_channel_bate\">Бета-версія</string>\n    <string name=\"update_channel_nightly\">Нічна збірка</string>\n    <string name=\"settings_xposed_api_call_protection\">Захист викликів Xposed API</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Блокування динамічно завантаженого коду модуля для використання Xposed API, це може призвести до поломки деяких модулів, але покращить безпеку</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Опис</string>\n    <string name=\"module_releases\">Релізи</string>\n    <string name=\"module_information\">Інформація</string>\n    <string name=\"module_information_homepage\">Домашня сторінка</string>\n    <string name=\"module_information_source_url\">Вихідний код</string>\n    <string name=\"module_information_collaborators\">Співавтори</string>\n    <string name=\"module_release_view_assets\">Вкладення</string>\n    <string name=\"menu_open_in_browser\">Відкрити в браузері</string>\n    <string name=\"module_release_load_more\">Показувати старіші версії</string>\n    <string name=\"module_release_no_more\">Більше релізів немає</string>\n    <string name=\"repo_load_failed\">Не вдалося завантажити модуль репозиторія: %s</string>\n    <string name=\"sort_upgradable_first\">Спершу нові версії</string>\n    <string name=\"installed\">встановлено</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d завантажити</item>\n        <item quantity=\"few\">%d завантажень</item>\n        <item quantity=\"many\">%d завантажень</item>\n        <item quantity=\"other\">%d завантажень</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Вишневий</string>\n    <string name=\"color_red\">Червоний</string>\n    <string name=\"color_pink\">Рожевий</string>\n    <string name=\"color_purple\">Фіолетовий</string>\n    <string name=\"color_deep_purple\">Темно-фіолетовий</string>\n    <string name=\"color_indigo\">Індиго</string>\n    <string name=\"color_blue\">Синій</string>\n    <string name=\"color_light_blue\">Світло-блакитний</string>\n    <string name=\"color_cyan\">Бірюзовий</string>\n    <string name=\"color_teal\">Зеленувато-блакитний</string>\n    <string name=\"color_green\">Зелений</string>\n    <string name=\"color_light_green\">Світло-зелений</string>\n    <string name=\"color_lime\">Лаймовий</string>\n    <string name=\"color_yellow\">Жовтий</string>\n    <string name=\"color_amber\">Бурштиновий</string>\n    <string name=\"color_orange\">Помаранчевий</string>\n    <string name=\"color_deep_orange\">Жовтогарячий</string>\n    <string name=\"color_brown\">Коричневий</string>\n    <string name=\"color_blue_grey\">Блакитно-сірий</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ur/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">جائزہ</string>\n    <string name=\"Modules\">ماڈیولز</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"one\">%d ماڈیولز آن ہیں۔</item>\n        <item quantity=\"other\">%d ماڈیولز فعال ہیں۔</item>\n    </plurals>\n    <string name=\"Logs\">نوشتہ جات</string>\n    <string name=\"Settings\">ترتیبات</string>\n    <string name=\"feedback_or_suggestion\">رائے یا تجویز</string>\n    <string name=\"About\">معلومات</string>\n    <string name=\"report_issue\">رپورٹ ایشو۔</string>\n    <string name=\"module_repo\">مخزن۔</string>\n    <string name=\"module_repo_up_to_date\">تمام ماڈیولز اپ ٹو ڈیٹ</string>\n    <string name=\"module_repo_published_time\">%sپر شائع ہوا۔</string>\n    <string name=\"module_repo_updated_time\">%sپر اپ ڈیٹ کیا گیا۔</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"one\">%d ماڈیول قابل اپ گریڈ</item>\n        <item quantity=\"other\">%d ماڈیولز قابل اپ گریڈ</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[ماخذ کوڈ دیکھیں %1$s<br/> ہمارے ساتھ شامل ہوں%2$s چینل۔]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">PGH Gaming Official</string>\n    <string name=\"install\">انسٹال</string>\n    <string name=\"install_summary\">کلک تو انسٹال LSposed</string>\n    <string name=\"not_installed\">یہ انسٹال نہیں ہے۔</string>\n    <string name=\"not_install_summary\">LSposed انسٹال نہیں ہے۔</string>\n    <string name=\"activated\">یہ چالو ہے۔</string>\n    <string name=\"partial_activated\">جزوی طور پر فعال</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SePolicy ٹھیک سے آن نہیں ہے</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[<a href=\"https://github.com/topjohnwu/Magisk/issues\"mggnteer.]]></string>\n    <string name=\"system_inject_fail_summary\">سسٹم فریم ورک انجکشن ناکام۔</string>\n    <string name=\"system_inject_fail\"><![CDATA[یہ نایاب ہے اور یہ <b>Magisk</b> یا کچھ کم معیار کے Magisk ماڈیولز کی وجہ سے ہو سکتا ہے۔<br/>براہ کرم Riru اور LSPosed کے علاوہ Magisk ماڈیولز کو غیر فعال کرنے کی کوشش کریں یا مکمل لاگ ڈویلپرز کو جمع کرائیں۔]]></string>\n    <string name=\"system_prop_incorrect_summary\">سسٹم پروپ غلط ہے۔</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[سسٹم کی کچھ ضروری خصوصیات حذف یا ترمیم کی گئیں۔<br/>ماڈیول کبھی کبھار باطل ہو سکتے ہیں۔]]></string>\n    <string name=\"need_update\">اپ ڈیٹ درکار ہے۔</string>\n    <string name=\"please_update_summary\">براہ کرم LSPosed کا تازہ ترین ورژن انسٹال کریں۔</string>\n    <string name=\"info_api_version\">API ورژن۔</string>\n    <string name=\"info_framework_version\">فریم ورک ورژن۔</string>\n    <string name=\"info_manager_package_name\">مینیجر پیکیج کا نام</string>\n    <string name=\"info_system_version\">سسٹم ورژن۔</string>\n    <string name=\"info_device\">ڈیوائس۔</string>\n    <string name=\"info_system_abi\">ABI سسٹم۔</string>\n    <string name=\"info_dex2oat_wrapper\">ڈیکس آپٹیمائزر ریپر</string>\n    <string name=\"enabled\">فعال</string>\n    <string name=\"not_enabled\">فعال نہیں ہے۔</string>\n    <string name=\"supported\">حمایت یافتہ</string>\n    <string name=\"unsupported\">غیر تعاون یافتہ</string>\n    <string name=\"android_version_unsatisfied\">Android ورژن غیر مطمئن ہے۔</string>\n    <string name=\"crashed\">کریش ہو گیا۔</string>\n    <string name=\"mount_failed\">ماؤنٹ ناکام ہوگیا۔</string>\n    <string name=\"selinux_permissive\">SELinux کی اجازت ہے۔</string>\n    <string name=\"sepolicy_incorrect\">SELinux پالیسی غلط ہے۔</string>\n    <string name=\"update_lsposed\">LSPosed کو اپ ڈیٹ کریں۔</string>\n    <string name=\"update_lsposed_msg\">LSPosed کو اپ ڈیٹ کرنے کی تصدیق کریں؟ یہ آلہ اپ ڈیٹ مکمل ہونے کے بعد دوبارہ شروع ہو جائے گا۔</string>\n    <string name=\"info_copied\">کلپ بورڈ پر کاپی ہو گیا۔</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">LSPosed میں خوش آمدید</string>\n    <string name=\"parasitic_welcome_summary\">آپ پرجیوی مینیجر کا استعمال کر رہے ہیں، جو شارٹ کٹ بنا سکتا ہے یا پھر بھی اطلاع سے کھل سکتا ہے۔</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">آپ پرجیوی مینیجر استعمال کر رہے ہیں، جو اطلاع سے کھل سکتا ہے۔</string>\n    <string name=\"create_shortcut\">شارٹ کٹ بنانا</string>\n    <string name=\"never_show\">کبھی نہ دکھائیں۔</string>\n    <string name=\"app_welcome\">پرجیوی مینیجر کی سفارش</string>\n    <string name=\"app_welcome_summary\">LSPosed اب پتہ لگانے سے بچنے کے لیے سسٹم پرجیوی کی حمایت کرتا ہے، آپ نوٹیفکیشن سے پرجیوی مینیجر کو کھول سکتے ہیں۔ موجودہ ایپلیکیشن کو ان انسٹال کرنے کی سفارش کی جاتی ہے۔</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">محفوظ کریں</string>\n    <string name=\"nav_item_logs_verbose\">وربوز لاگز</string>\n    <string name=\"nav_item_logs_module\">ماڈیولز نوشتہ جات</string>\n    <string name=\"logs_saving\">لاگ محفوظ کر رہا ہے، براہ کرم انتظار کریں۔</string>\n    <string name=\"logs_saved\">لاگز محفوظ ہو گئے۔</string>\n    <string name=\"logs_save_failed2\">محفوظ کرنے میں ناکام:\\n%s</string>\n    <string name=\"menuClearLog\">ابھی لاگ صاف کریں۔</string>\n    <string name=\"logs_cleared\">لاگ کامیابی سے صاف ہو گیا.</string>\n    <string name=\"scroll_top\">اوپر سکرول کریں۔</string>\n    <string name=\"loading\">لوڈ ہو رہا ہے…</string>\n    <string name=\"scroll_bottom\">نیچے سکرول کریں۔</string>\n    <string name=\"menuReload\">دوبارہ لوڈ کریں۔</string>\n    <string name=\"logs_clear_failed_2\">لاگ کو صاف کرنے میں ناکام</string>\n    <string name=\"menu_enable_word_wrap\">لفظ لفاف</string>\n    <string name=\"enabled_verbose_log\">وربوز لاگ فعال ہے۔</string>\n    <string name=\"disabled_verbose_log\">وربوز لاگ غیر فعال ہے۔</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(کوئی تفصیل فراہم نہیں کی گئی)</string>\n    <string name=\"warning_xposed_min_version\">اس ماڈیول کے لیے ایک نئے Xposed ورژن (%d) کی ضرورت ہے اور اس طرح اسے چالو نہیں کیا جا سکتا</string>\n    <string name=\"warning_target_version_higher\">یہ ماڈیول ایک نئے Xposed ورژن (%d) کے لیے ڈیزائن کیا گیا ہے اور اس طرح کچھ افعال کام نہیں کر سکتے</string>\n    <string name=\"no_min_version_specified\">یہ ماڈیول اس Xposed ورژن کی وضاحت نہیں کرتا جس کی اسے ضرورت ہے۔</string>\n    <string name=\"warning_min_version_too_low\">یہ ماڈیول Xposed ورژن %1$dکے لیے بنایا گیا تھا، لیکن ورژن %2$dمیں غیر موافق تبدیلیوں کی وجہ سے، اسے غیر فعال کر دیا گیا ہے۔</string>\n    <string name=\"warning_installed_on_external_storage\">اس ماڈیول کو لوڈ نہیں کیا جا سکتا کیونکہ یہ SD کارڈ پر انسٹال ہے، براہ کرم اسے اندرونی اسٹوریج میں منتقل کریں۔</string>\n    <string name=\"module_uninstall\">ان انسٹال کریں۔</string>\n    <string name=\"module_settings\">ماڈیول کی ترتیبات</string>\n    <string name=\"view_in_repo\">ریپو میں دیکھیں</string>\n    <string name=\"module_uninstall_message\">کیا آپ اس ماڈیول کو ان انسٹال کرنا چاہتے ہیں؟</string>\n    <string name=\"module_uninstalled\">ان انسٹال %1$s</string>\n    <string name=\"module_uninstall_failed\">اَن انسٹال ناکام</string>\n    <string name=\"add_module_to_user\">صارف میں ماڈیول شامل کریں۔</string>\n    <string name=\"module_installed\">صارف %2$sمیں %1$s شامل کیا گیا۔</string>\n    <string name=\"module_install_failed\">ماڈیول شامل کرنا ناکام ہو گیا۔</string>\n    <string name=\"install_to_user\">صارف %sپر انسٹال کریں۔</string>\n    <string name=\"install_to_user_message\">صارف %2$sپر %1$s انسٹال کرنا چاہتے ہیں؟ یہ دستی طور پر انسٹال کرنے کی سفارش کی جاتی ہے، LSPosed کے ذریعے انسٹال کرنے سے مسائل پیدا ہو سکتے ہیں۔</string>\n    <string name=\"expand\">پھیلاؤ</string>\n    <string name=\"collapse\">گرنے</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">دوبارہ اصلاح کریں۔</string>\n    <string name=\"compile_speed_msg\">…کو بہتر بنانا</string>\n    <string name=\"compile_done\">اصلاح مکمل</string>\n    <string name=\"app_launch\">اسے لانچ کریں۔</string>\n    <string name=\"compile_failed\">اصلاح ناکام: واپسی کی قدر خالی ہے۔</string>\n    <string name=\"compile_failed_with_info\">اصلاح ناکام: </string>\n    <string name=\"sort_by_name\">درخواست کا نام</string>\n    <string name=\"sort_by_package_name\">پیکیج کا نام</string>\n    <string name=\"sort_by_install_time\">انسٹال کرنے کا وقت</string>\n    <string name=\"sort_by_update_time\">اپ ڈیٹ کا وقت</string>\n    <string name=\"sort_reverse\">معکوس</string>\n    <string name=\"menu_show_system_apps\">سسٹم ایپس</string>\n    <string name=\"menu_sort\">چھانٹنا</string>\n    <string name=\"enable_module\">ماڈیول کو فعال کریں۔</string>\n    <string name=\"no_scope_selected\">آپ نے کوئی ایپ منتخب نہیں کی۔ جاری رہے؟</string>\n    <string name=\"menu_show_games\">کھیل</string>\n    <string name=\"menu_show_modules\">ماڈیولز</string>\n    <string name=\"menu_show_denylist\">انکار کرنے والا</string>\n    <string name=\"failed_to_save_scope_list\">دائرہ کار کی فہرست کو محفوظ کرنے میں ناکام</string>\n    <string name=\"app_version\">ورژن: %1$s</string>\n    <string name=\"use_recommended\">سفارش کریں۔</string>\n    <string name=\"no_scope_selected_has_recommended\">آپ نے کوئی ایپ منتخب نہیں کی۔ تجویز کردہ ایپس کو منتخب کریں؟</string>\n    <string name=\"use_recommended_message\">تجویز کردہ ایپس کو منتخب کریں?</string>\n    <string name=\"module_is_not_activated_yet\">Xposed ماڈیول ابھی تک چالو نہیں ہوا ہے۔</string>\n    <string name=\"requested_by_module\">سفارش کریں۔</string>\n    <string name=\"update_available\">اپ ڈیٹ دستیاب ہے: %1$s</string>\n    <string name=\"module_disabled_no_selection\">ماڈیول%s کو غیر فعال کر دیا گیا ہے کیونکہ کوئی ایپ منتخب نہیں کی گئی ہے.</string>\n    <string name=\"android_framework\">سسٹم فریم ورک</string>\n    <string name=\"menu_backup_and_restore\">بیک اپ</string>\n    <string name=\"menu_backup\">بیک اپ</string>\n    <string name=\"menu_restore\">بحال کریں۔</string>\n    <string name=\"force_stop\">زبردستی روکنا</string>\n    <string name=\"force_stop_dlg_title\">زبردستی روکنا؟</string>\n    <string name=\"force_stop_dlg_text\">اگر آپ کسی ایپ کو زبردستی روکتے ہیں تو یہ غلط برتاؤ کر سکتی ہے۔</string>\n    <string name=\"reboot_required\">اس تبدیلی کو لاگو کرنے کے لیے ریبوٹ کی ضرورت ہے۔</string>\n    <string name=\"reboot\">دوبارہ شروع کریں۔</string>\n    <string name=\"menu_hide\">چھپائیں</string>\n    <string name=\"deny_list\">%s انکاری فہرست میں ہے۔ ہو سکتا ہے اس کا اثر نہ ہو۔</string>\n    <string name=\"deny_list_info\">انکار کرنے والے پر</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">دوسری ایپ میں دیکھیں</string>\n    <string name=\"module_app_info\">ایپ کی معلومات</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nیہاں کچھ نہیں ہے۔</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">فریم ورک</string>\n    <string name=\"settings_disable_verbose_log\">وربوز لاگز کو غیر فعال کریں۔</string>\n    <string name=\"settings_disable_verbose_log_summary\">وربوز لاگز کو شامل کرنے کے لیے مسائل کی اطلاع دیں۔</string>\n    <string name=\"pure_black_dark_theme\">سیاہ سیاہ تھیم</string>\n    <string name=\"pure_black_dark_theme_summary\">اگر سیاہ تھیم فعال ہو تو خالص سیاہ تھیم استعمال کریں۔</string>\n    <string name=\"settings_group_theme\">خیالیہ</string>\n    <string name=\"settings_backup_and_restore\">بیک اپ اور بحال</string>\n    <string name=\"settings_backup_summery\">بیک اپ ماڈیول کی فہرست اور دائرہ کار کی فہرستیں۔</string>\n    <string name=\"settings_restore_summery\">ماڈیول کی فہرست اور دائرہ کار کی فہرستوں کو بحال کریں۔</string>\n    <string name=\"settings_backup\">بیک اپ</string>\n    <string name=\"settings_backup_failed2\">بیک اپ کرنے میں ناکام:\\n%s</string>\n    <string name=\"enable_documentui\">براہ کرم DocumentUI کو فعال کریں۔</string>\n    <string name=\"settings_restore\">بحال کریں۔</string>\n    <string name=\"settings_restore_failed2\">بحال کرنے میں ناکام:\\n%s</string>\n    <string name=\"group_network\">نیٹ ورک</string>\n    <string name=\"dns_over_http\">HTTPS پر DNS</string>\n    <string name=\"dns_over_http_summary\">کچھ قوموں میں DNS زہر کا حل</string>\n    <string name=\"theme_color\">تھیم کا رنگ</string>\n    <string name=\"theme_color_system\">سسٹم تھیم کا رنگ</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">ایپس کو لانچر آئیکنز دکھانے پر مجبور کریں۔</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">اینڈرائیڈ 10 کے بعد ایپس کو اپنے لانچر آئیکنز کو چھپانے کی اجازت نہیں ہے۔ اس سسٹم کی خصوصیت کو غیر فعال کرنے کے لیے ٹوگل کو بند کریں۔</string>\n    <string name=\"settings_group_system\">سسٹم</string>\n    <string name=\"settings_language\">زبان</string>\n    <string name=\"settings_translation_contributors\">ترجمہ کرنے والے</string>\n    <string name=\"settings_translation\">ترجمہ میں حصہ لیں۔</string>\n    <string name=\"settings_translation_summary\">%s کا اپنی زبان میں ترجمہ کرنے میں ہماری مدد کریں۔</string>\n    <string name=\"settings_create_shortcut_summary\">ایک شارٹ کٹ بنائیں جو پرجیوی مینیجر کو کھول سکے۔</string>\n    <string name=\"settings_shortcut_pinned_hint\">شارٹ کٹ پن کر دیا گیا۔</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">موجودہ ڈیفالٹ لانچر پن شارٹ کٹس کو سپورٹ نہیں کرتا ہے۔</string>\n    <string name=\"settings_enable_status_notification\">اسٹیٹس کی اطلاع</string>\n    <string name=\"settings_enable_status_notification_summary\">ایک اطلاع دکھائیں جو پرجیوی مینیجر کو کھول سکے۔</string>\n    <string name=\"disable_status_notification_error\">کوئی شارٹ کٹ نہیں، اطلاع کو غیر فعال نہیں کر سکتا</string>\n    <string name=\"settings_update_channel\">چینل کو اپ ڈیٹ کریں۔</string>\n    <string name=\"update_channel_stable\">مستحکم</string>\n    <string name=\"update_channel_bate\">بیٹا</string>\n    <string name=\"update_channel_nightly\">رات کی تعمیر</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API کال تحفظ</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">ایکسپوزڈ API استعمال کرنے کے لیے متحرک طور پر بھرے ہوئے ماڈیول کوڈ کو بلاک کریں، اس سے کچھ ماڈیول ٹوٹ سکتے ہیں لیکن سیکیورٹی کو فائدہ پہنچ سکتا ہے</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">ریڈمی</string>\n    <string name=\"module_releases\">جاری کرتا ہے۔</string>\n    <string name=\"module_information\">معلومات</string>\n    <string name=\"module_information_homepage\">ہوم پیج</string>\n    <string name=\"module_information_source_url\">سورس کوڈ</string>\n    <string name=\"module_information_collaborators\">تعاون کرنے والے</string>\n    <string name=\"module_release_view_assets\">اثاثے</string>\n    <string name=\"menu_open_in_browser\">براؤزر میں کھولیں</string>\n    <string name=\"module_release_load_more\">پرانے ورژن دکھائیں۔</string>\n    <string name=\"module_release_no_more\">مزید رہائی نہیں ہے۔</string>\n    <string name=\"repo_load_failed\">ماڈیول ریپو لوڈ کرنے میں ناکام: %s</string>\n    <string name=\"sort_upgradable_first\">پہلے اپ گریڈ کیا جا سکتا ہے۔</string>\n    <string name=\"installed\">انسٹال</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"one\">%d ڈاؤن لوڈ</item>\n        <item quantity=\"other\">%d ڈاؤن لوڈز</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">ساکورا</string>\n    <string name=\"color_red\">سرخ</string>\n    <string name=\"color_pink\">گلابی</string>\n    <string name=\"color_purple\">جامنی</string>\n    <string name=\"color_deep_purple\">گہرے جامنی رنگ</string>\n    <string name=\"color_indigo\">انڈگو</string>\n    <string name=\"color_blue\">نیلا</string>\n    <string name=\"color_light_blue\">ہلکے نیلے رنگ کے</string>\n    <string name=\"color_cyan\">سیان</string>\n    <string name=\"color_teal\">ٹیل</string>\n    <string name=\"color_green\">سبز</string>\n    <string name=\"color_light_green\">ہلکا سبز</string>\n    <string name=\"color_lime\">لیموں</string>\n    <string name=\"color_yellow\">پیلا</string>\n    <string name=\"color_amber\">امبر</string>\n    <string name=\"color_orange\">کینو</string>\n    <string name=\"color_deep_orange\">گہرا نارنجی</string>\n    <string name=\"color_brown\">براؤن</string>\n    <string name=\"color_blue_grey\">بلیو گرے</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v28/dimens.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<resources>\n    <dimen name=\"home_primary_elevation\">12dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v28/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<resources>\n    <style name=\"Theme.Light\" parent=\"Base.AppTheme.Light\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n\n    <style name=\"Theme\" parent=\"Base.AppTheme\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v29/settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<resources>\n    <bool name=\"show_system_settings\">true</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v30/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<resources>\n    <style name=\"Theme.Light\" parent=\"Base.AppTheme.Light\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">always</item>\n    </style>\n\n    <style name=\"Theme\" parent=\"Base.AppTheme\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">always</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v31/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2023 LSPosed Contributors\n  -->\n\n<resources>\n    <color name=\"ic_launcher_foreground\">@android:color/system_accent1_0</color>\n    <color name=\"ic_launcher_background\">@android:color/system_accent1_600</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">Tổng quan</string>\n    <string name=\"Modules\">Mô-đun</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">%d Mô-đun đã bật</item>\n    </plurals>\n    <string name=\"Logs\">Nhật ký</string>\n    <string name=\"Settings\">Cài đặt</string>\n    <string name=\"feedback_or_suggestion\">Phản hồi hoặc góp ý</string>\n    <string name=\"About\">Giới thiệu</string>\n    <string name=\"report_issue\">Báo cáo sự cố</string>\n    <string name=\"module_repo\">Kho</string>\n    <string name=\"module_repo_up_to_date\">Tất cả các modules đã được cập nhật</string>\n    <string name=\"module_repo_published_time\">Xuất bản lúc %s</string>\n    <string name=\"module_repo_updated_time\">Cập nhật lúc %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d Modules có bản cập nhật</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[Xem mã nguồn tại %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">The Primal Pea, Tuyen Nguyen (tuyennn)</string>\n    <string name=\"install\">Cài đặt</string>\n    <string name=\"install_summary\">Ấn vào đây để cài đặt LSPosed</string>\n    <string name=\"not_installed\">Chưa được cài đặt</string>\n    <string name=\"not_install_summary\">LSPosed chưa được cài đặt</string>\n    <string name=\"activated\">Đã được kích hoạt</string>\n    <string name=\"partial_activated\">Một phần đã được kích hoạt</string>\n    <string name=\"selinux_policy_not_loaded_summary\">Chính sách SELinux không được nạp đúng cách</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[Vài tính năng không hoạt động.<br/>Vui lòng báo cáo đến <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>nhà phát triển</b></a>.]]></string>\n    <string name=\"system_inject_fail_summary\">Không thể nhúng vào khung hệ thống</string>\n    <string name=\"system_inject_fail\"><![CDATA[Trường hợp này khá hiếm khi xảy ra và có thể lý do bởi <b>Magisk</b> hoặc 1 số mô-đun Magisk kém chất lượng.<br/>Hãy thử vô hiệu hóa những mô-đun Magisk đó, thử chạy riêng Riru và LSPosed mà thôi hoặc thông báo nhật ký tới những nhà phát triển.]]></string>\n    <string name=\"system_prop_incorrect_summary\">Thông tin hệ thống không đúng</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[Một số thuộc tính hệ thống cần thiết đã bị xóa hoặc sửa đổi.<br/> Đôi khi, các mô-đun có thể sẽ mất hiệu lực.]]></string>\n    <string name=\"need_update\">Cần được cập nhật</string>\n    <string name=\"please_update_summary\">Vui lòng cài đặt phiên bản mới nhất của LSPosed</string>\n    <string name=\"developer_warning_title\">Mẹo cho module developer</string>\n    <string name=\"developer_warning_summary\">Vui lòng tắt tối ưu hóa triển khai trên Android Studio hoặc sử dụng lệnh `gradlew installDebug` để cài đặt. Nếu không, apk mô-đun sẽ không được cập nhật.</string>\n    <string name=\"info_api_version\">Phiên bản API</string>\n    <string name=\"info_framework_version\">Phiên bản Framework</string>\n    <string name=\"info_manager_package_name\">Tên gói quản lý</string>\n    <string name=\"info_system_version\">Phiên bản hệ thống</string>\n    <string name=\"info_device\">Thiết bị</string>\n    <string name=\"info_system_abi\">Hệ thống ABI</string>\n    <string name=\"info_dex2oat_wrapper\">Trình tối ưu Dex Wrapper</string>\n    <string name=\"enabled\">Đã bật</string>\n    <string name=\"not_enabled\">Chưa bật</string>\n    <string name=\"supported\">Được hỗ trợ</string>\n    <string name=\"unsupported\">Không được hỗ trợ</string>\n    <string name=\"android_version_unsatisfied\">Phiên bản Android không hỗ trợ</string>\n    <string name=\"crashed\">Bị lỗi</string>\n    <string name=\"mount_failed\">Mount thất bại</string>\n    <string name=\"selinux_permissive\">Cho phép SELinux</string>\n    <string name=\"sepolicy_incorrect\">Chính sách SELinux không chính xác</string>\n    <string name=\"update_lsposed\">Cập nhật LSPosed</string>\n    <string name=\"update_lsposed_msg\">Xác nhận cập nhật LSPosed? Thiết bị này sẽ khởi động lại sau khi hoàn tất cập nhật</string>\n    <string name=\"info_copied\">Đã sao chép vào bảng nhớ tạm</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">Chào mừng</string>\n    <string name=\"parasitic_welcome_summary\">Đang sử dụng trình quản lý phụ thuộc có thể tạo lối tắt hoặc mở từ thông báo.</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">Đang sử dụng trình quản lý phụ thuộc có thể mở từ thông báo.</string>\n    <string name=\"create_shortcut\">Tạo lối tắt</string>\n    <string name=\"never_show\">Không hiện nữa</string>\n    <string name=\"app_welcome\">Quản lý phụ thuộc khuyến nghị</string>\n    <string name=\"app_welcome_summary\">Ứng dụng hiện hỗ trợ phụ thuộc hệ thống để tránh bị phát hiện có thể mở trình quản lý từ thông báo.\nNên gỡ cài đặt ứng dụng hiện tại.</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">Lưu lại</string>\n    <string name=\"nav_item_logs_verbose\">Nhật ký Chi tiết</string>\n    <string name=\"nav_item_logs_module\">Nhật ký các Mô-đun</string>\n    <string name=\"logs_saving\">Đang lưu nhật ký, vui lòng đợi</string>\n    <string name=\"logs_saved\">Nhật ký đã được lưu</string>\n    <string name=\"logs_save_failed2\">Lưu thất bại:\\n%s</string>\n    <string name=\"menuClearLog\">Xóa nhật ký</string>\n    <string name=\"logs_cleared\">Nhật ký đã được xoá.</string>\n    <string name=\"scroll_top\">Cuộn lên trên</string>\n    <string name=\"loading\">Đang tải…</string>\n    <string name=\"scroll_bottom\">Cuộn xuống dưới</string>\n    <string name=\"menuReload\">Tải lại</string>\n    <string name=\"logs_clear_failed_2\">Xoá nhật kí thất bại</string>\n    <string name=\"menu_enable_word_wrap\">Tự động xuống dòng</string>\n    <string name=\"enabled_verbose_log\">Nhật ký chi tiết đã được kích hoạt</string>\n    <string name=\"disabled_verbose_log\">Nhật ký chi tiết đã được vô hiệu hoá</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(chưa có mô tả)</string>\n    <string name=\"warning_xposed_min_version\">Mô-đun này yêu cầu một phiên bản Xposed mới hơn (%d) và đó là lý do không thể được kích hoạt</string>\n    <string name=\"warning_target_version_higher\">Tiện ích bổ sung này được làm cho phiên bản ứng dụng mới hơn (%d) nên một số chức năng có thể không hoạt động</string>\n    <string name=\"no_min_version_specified\">Mô-đun này không chỉ định phiên bản Xposed cần thiết để khởi chạy.</string>\n    <string name=\"warning_min_version_too_low\">Mô-đun này được tạo bởi phiên bản Xposed %1$d, nhưng vì lý do không tương thích với phiên bản %2$d, nên nó đã bị vô hiệu hóa</string>\n    <string name=\"warning_installed_on_external_storage\">Mô-đun này không được nạp vì nó được cài đặt trên thẻ nhớ SD, vui lòng chuyển nó vào bộ nhớ trong</string>\n    <string name=\"module_uninstall\">Gỡ cài đặt</string>\n    <string name=\"module_settings\">Cài đặt Mô-đun</string>\n    <string name=\"view_in_repo\">Xem ở trên Kho</string>\n    <string name=\"module_uninstall_message\">Bạn có muốn gỡ cài đặt mô-đun này?</string>\n    <string name=\"module_uninstalled\">Đã gỡ cài đặt %1$s</string>\n    <string name=\"module_uninstall_failed\">Gỡ cài đặt không thành công</string>\n    <string name=\"add_module_to_user\">Thêm mô-đun tới người dùng</string>\n    <string name=\"module_installed\">Đã thêm %1$s tới người dùng %2$s</string>\n    <string name=\"module_install_failed\">Thêm mô-đun thất bại</string>\n    <string name=\"install_to_user\">Cài đặt tới người dùng %s</string>\n    <string name=\"install_to_user_message\">Muốn cài %1$s tới người dùng %2$s? Khuyến cáo bạn nên cài đặt thủ công, buộc cài đặt qua LSPosed có thể xảy ra vấn đề không mong muốn.</string>\n    <string name=\"expand\">mở</string>\n    <string name=\"collapse\">đóng</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">Tối ưu lại</string>\n    <string name=\"compile_speed_msg\">Đang tối ưu…</string>\n    <string name=\"compile_done\">Tối ưu hoàn tất</string>\n    <string name=\"app_launch\">Khởi chạy</string>\n    <string name=\"compile_failed\">Tối ưu thất bại: trả về giá trị trống</string>\n    <string name=\"compile_failed_with_info\">Tôi ưu thất bại: </string>\n    <string name=\"sort_by_name\">Tên ứng dụng</string>\n    <string name=\"sort_by_package_name\">Tên gói ứng dụng</string>\n    <string name=\"sort_by_install_time\">Thời gian cài đặt</string>\n    <string name=\"sort_by_update_time\">Thời gian cập nhật</string>\n    <string name=\"sort_reverse\">Đảo ngược</string>\n    <string name=\"menu_show_system_apps\">Ứng dụng hệ thống</string>\n    <string name=\"menu_sort\">Sắp xếp</string>\n    <string name=\"enable_module\">Kích hoạt mô-đun</string>\n    <string name=\"no_scope_selected\">Bạn đã không lựa chọn bất kỳ ứng dụng nào. Tiếp tục chứ?</string>\n    <string name=\"menu_show_games\">Trò chơi</string>\n    <string name=\"menu_show_modules\">Mô-đun</string>\n    <string name=\"menu_show_denylist\">Danh sách từ chối</string>\n    <string name=\"failed_to_save_scope_list\">Lưu danh sách phạm vi thất bại</string>\n    <string name=\"app_version\">Phiên bản: %1$s</string>\n    <string name=\"use_recommended\">Được khuyến cáo</string>\n    <string name=\"no_scope_selected_has_recommended\">Bạn đã không lựa chọn bất kỳ ứng dụng nào. Lựa chọn những ứng dụng được khuyến nghị?</string>\n    <string name=\"use_recommended_message\">Lựa chọn những ứng dụng được khuyến cáo?</string>\n    <string name=\"module_is_not_activated_yet\">Mô-đun Xposed chưa được kích hoạt</string>\n    <string name=\"requested_by_module\">Được khuyến cáo</string>\n    <string name=\"update_available\">Cập nhật khả dụng: %1$s</string>\n    <string name=\"module_disabled_no_selection\">Mô-đun %s đã bị vô hiệu hoá do không có ứng dụng nào được lựa chọn.</string>\n    <string name=\"android_framework\">Framework Hệ thống</string>\n    <string name=\"menu_backup_and_restore\">Sao lưu</string>\n    <string name=\"menu_backup\">Sao lưu</string>\n    <string name=\"menu_restore\">Phục hồi</string>\n    <string name=\"force_stop\">Buộc dừng</string>\n    <string name=\"force_stop_dlg_title\">Buộc dừng?</string>\n    <string name=\"force_stop_dlg_text\">Nếu bạn buộc dừng một ứng dụng, nó có thể gặp lỗi.</string>\n    <string name=\"reboot_required\">Khởi động lại là bắt buộc cho thay đổi này</string>\n    <string name=\"reboot\">Khởi động lại</string>\n    <string name=\"menu_hide\">Ẩn</string>\n    <string name=\"deny_list\">%s đã trong danh sách từ chối. Nó có thể không có hiệu lực.</string>\n    <string name=\"deny_list_info\">Trong danh sách từ chối</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">Xem trong ứng dụng khác</string>\n    <string name=\"module_app_info\">Thông tin ứng dụng</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\nKhông có gì ở đây cả</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">Khung hệ thống</string>\n    <string name=\"settings_disable_verbose_log\">Vô hiệu hoá nhật ký chi tiết</string>\n    <string name=\"settings_disable_verbose_log_summary\">Báo cáo sự cố yêu cầu bao gồm nhật ký chi tiết</string>\n    <string name=\"settings_enable_log_watchdog\">Bật chức năng giám sát nhật ký</string>\n    <string name=\"settings_enable_log_watchdog_summary\">Log watchdog sửa đổi các thuộc tính hệ thống của LSPosed, có thể được khai thác để phát hiện LSPosed</string>\n    <string name=\"pure_black_dark_theme\">Chủ đề Đen - Tối</string>\n    <string name=\"pure_black_dark_theme_summary\">Sử dụng chủ đề đen nếu chủ đề tối được bật</string>\n    <string name=\"settings_group_theme\">Chủ đề</string>\n    <string name=\"settings_backup_and_restore\">Sao lưu và khôi phục</string>\n    <string name=\"settings_backup_summery\">Sao lưu danh sách mô-đun và danh sách phạm vi.</string>\n    <string name=\"settings_restore_summery\">Khôi phục danh sách mô-đun và danh sách phạm vi.</string>\n    <string name=\"settings_backup\">Sao lưu</string>\n    <string name=\"settings_backup_failed2\">Sao lưu thất bại:\\n%s</string>\n    <string name=\"enable_documentui\">Vui lòng kích hoạt DocumentUI</string>\n    <string name=\"settings_restore\">Phục hồi</string>\n    <string name=\"settings_restore_failed2\">Phục hồi thất bại:\\n%s</string>\n    <string name=\"group_network\">Mạng</string>\n    <string name=\"dns_over_http\">DNS qua HTTPS</string>\n    <string name=\"dns_over_http_summary\">Giải pháp khắc phục tình trạng giả mạo DNS ở một số quốc gia</string>\n    <string name=\"theme_color\">Màu chủ đề</string>\n    <string name=\"theme_color_system\">Màu chủ đề hệ thống</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">Buộc các ứng dụng hiển thị biểu tượng trên trình khởi chạy</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Sau Android 10, các ứng dụng không được phép ẩn biểu tượng trên trình khởi chạy. Tắt lựa chọn này để vô hiệu tính năng này của hệ thống.</string>\n    <string name=\"settings_group_system\">Hệ thống</string>\n    <string name=\"settings_language\">Ngôn ngữ</string>\n    <string name=\"settings_translation_contributors\">Cộng tác viên phiên dịch</string>\n    <string name=\"settings_translation\">Tham gia phiên dịch</string>\n    <string name=\"settings_translation_summary\">Giúp chúng tôi dịch %s sang ngôn ngữ của bạn</string>\n    <string name=\"settings_create_shortcut_summary\">Tạo lối tắt để mở trình quản lý phụ thuộc</string>\n    <string name=\"settings_shortcut_pinned_hint\">Đã ghim phím tắt</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">Trình khởi chạy hiện tại không hỗ trợ tạo lối tắt</string>\n    <string name=\"settings_enable_status_notification\">Thông báo trạng thái</string>\n    <string name=\"settings_enable_status_notification_summary\">Hiện thông báo để mở trình quản lý phụ thuộc</string>\n    <string name=\"disable_status_notification_error\">Không có lối tắt nên không thể tắt thông báo</string>\n    <string name=\"settings_update_channel\">Kênh cập nhật</string>\n    <string name=\"update_channel_stable\">Ổn định</string>\n    <string name=\"update_channel_bate\">Thử nghiệm</string>\n    <string name=\"update_channel_nightly\">Bản dựng hàng đêm</string>\n    <string name=\"settings_xposed_api_call_protection\">Bảo vệ kết nối</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">Chặn mã tiện ích hoạt động được sử dụng.\nCó thể làm hỏng một số tiện ích bổ sung nhưng có lợi cho bảo mật</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">Đọc</string>\n    <string name=\"module_releases\">Bản phát hành</string>\n    <string name=\"module_information\">Thông tin</string>\n    <string name=\"module_information_homepage\">Trang chủ</string>\n    <string name=\"module_information_source_url\">Mã nguồn</string>\n    <string name=\"module_information_collaborators\">Cộng tác viên</string>\n    <string name=\"module_release_view_assets\">Tài sản</string>\n    <string name=\"menu_open_in_browser\">Mở trong trình duyệt</string>\n    <string name=\"module_release_load_more\">Hiển thị các phiên bản cũ hơn</string>\n    <string name=\"module_release_no_more\">Không có phiên bản nào</string>\n    <string name=\"repo_load_failed\">Tải kho lưu trữ mô-đun thất bại: %s</string>\n    <string name=\"sort_upgradable_first\">Có thể nâng cấp trước</string>\n    <string name=\"installed\">Đã được cài đặt</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d lượt tải xuống</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">Hoa anh đào</string>\n    <string name=\"color_red\">Đỏ</string>\n    <string name=\"color_pink\">Hồng</string>\n    <string name=\"color_purple\">Tím</string>\n    <string name=\"color_deep_purple\">Tím đậm</string>\n    <string name=\"color_indigo\">Xanh đậm</string>\n    <string name=\"color_blue\">Xanh</string>\n    <string name=\"color_light_blue\">Xanh sáng</string>\n    <string name=\"color_cyan\">Lục lam</string>\n    <string name=\"color_teal\">Mòng két</string>\n    <string name=\"color_green\">Xanh lá</string>\n    <string name=\"color_light_green\">Xanh lá sáng</string>\n    <string name=\"color_lime\">Xanh chanh</string>\n    <string name=\"color_yellow\">Vàng</string>\n    <string name=\"color_amber\">Hổ phách</string>\n    <string name=\"color_orange\">Cam</string>\n    <string name=\"color_deep_orange\">Cam đậm</string>\n    <string name=\"color_brown\">Nâu</string>\n    <string name=\"color_blue_grey\">Xanh xám</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">概览</string>\n    <string name=\"Modules\">模块</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">已启用 %d 个模块</item>\n    </plurals>\n    <string name=\"Logs\">日志</string>\n    <string name=\"Settings\">设置</string>\n    <string name=\"feedback_or_suggestion\">反馈或建议</string>\n    <string name=\"About\">关于</string>\n    <string name=\"report_issue\">反馈问题</string>\n    <string name=\"module_repo\">仓库</string>\n    <string name=\"module_repo_up_to_date\">所有模块均已最新</string>\n    <string name=\"module_repo_published_time\">发布于 %s</string>\n    <string name=\"module_repo_updated_time\">更新于 %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d 个模块可更新</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[在 %1$s 查看源码<br/>加入我们的 %2$s 频道<br/>加入我们的 <b><a href=\"mqqguild://guild/share?inviteCode=Xz9dJ&from=246610\">QQ 频道</a></b>]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">LSPosed\nJingMatrix</string>\n    <string name=\"install\">安装</string>\n    <string name=\"install_summary\">点击安装 LSPosed</string>\n    <string name=\"not_installed\">未安装</string>\n    <string name=\"not_install_summary\">LSPosed 未安装</string>\n    <string name=\"activated\">已激活</string>\n    <string name=\"partial_activated\">部分激活</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy 未被正确加载</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[某些功能将不起作用。<br/>请将此问题报告给 <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> 开发者]]></string>\n    <string name=\"system_inject_fail_summary\">系统框架注入失败</string>\n    <string name=\"system_inject_fail\"><![CDATA[这是极罕见的情况，可能是由 <b>Magisk</b> 或低质 Magisk 模块导致。<br/>请尝试禁用除 Riru 和 LSPosed 外的其他 Magisk 模块，或向开发者提供完整日志。]]></string>\n    <string name=\"system_prop_incorrect_summary\">系统属性异常</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[一些必须的系统属性被删除或被修改。<br/>模块可能会随机失效。]]></string>\n    <string name=\"need_update\">需要更新</string>\n    <string name=\"please_update_summary\">请安装新版 LSPosed</string>\n    <string name=\"developer_warning_title\">给模块开发者的提示</string>\n    <string name=\"developer_warning_summary\">请在 Android Studio 上禁用部署优化，或使用 `gradlew installDebug` 命令进行安装，否则无法更新模块。</string>\n    <string name=\"info_api_version\">API 版本</string>\n    <string name=\"info_framework_version\">框架版本</string>\n    <string name=\"info_manager_package_name\">管理器包名</string>\n    <string name=\"info_system_version\">系统版本</string>\n    <string name=\"info_device\">设备</string>\n    <string name=\"info_system_abi\">系统架构</string>\n    <string name=\"info_dex2oat_wrapper\">Dex 优化器包装</string>\n    <string name=\"enabled\">已启用</string>\n    <string name=\"not_enabled\">未启用</string>\n    <string name=\"supported\">支持</string>\n    <string name=\"unsupported\">不支持</string>\n    <string name=\"android_version_unsatisfied\">系统版本不受支持</string>\n    <string name=\"crashed\">崩溃</string>\n    <string name=\"mount_failed\">挂载失败</string>\n    <string name=\"selinux_permissive\">SELinux 处于宽容模式</string>\n    <string name=\"sepolicy_incorrect\">SELinux 规则异常</string>\n    <string name=\"update_lsposed\">更新 LSPosed</string>\n    <string name=\"update_lsposed_msg\">确认更新 LSPosed? 设备将会在完成更新后自动重启。</string>\n    <string name=\"info_copied\">已复制到剪贴板</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">欢迎使用 LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">你正在使用寄生管理器，可创建快捷方式或继续从通知中打开。</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">你正在使用寄生管理器，可以从通知打开它。</string>\n    <string name=\"create_shortcut\">创建快捷方式</string>\n    <string name=\"never_show\">不再显示</string>\n    <string name=\"app_welcome\">推荐使用寄生管理器</string>\n    <string name=\"app_welcome_summary\">LSPosed 现在支持系统寄生以避免检测，你可以从通知中打开寄生管理器。建议卸载当前应用。</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">保存</string>\n    <string name=\"nav_item_logs_verbose\">详细日志</string>\n    <string name=\"nav_item_logs_module\">模块日志</string>\n    <string name=\"logs_saving\">正在保存日志，请稍后</string>\n    <string name=\"logs_saved\">日志已保存</string>\n    <string name=\"logs_save_failed2\">保存失败：\\n%s</string>\n    <string name=\"menuClearLog\">立即清空日志</string>\n    <string name=\"logs_cleared\">成功清空日志。</string>\n    <string name=\"scroll_top\">滚动到顶部</string>\n    <string name=\"loading\">加载中…</string>\n    <string name=\"scroll_bottom\">滚动到底部</string>\n    <string name=\"menuReload\">重新加载</string>\n    <string name=\"logs_clear_failed_2\">日志清空失败</string>\n    <string name=\"menu_enable_word_wrap\">自动换行</string>\n    <string name=\"enabled_verbose_log\">详细日志已启用</string>\n    <string name=\"disabled_verbose_log\">详细日志已禁用</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">（未提供介绍）</string>\n    <string name=\"warning_xposed_min_version\">此模块需要更新的 Xposed 版本（%d），因此无法激活</string>\n    <string name=\"warning_target_version_higher\">此模块是为较新的 Xposed 版本（%d）设计的，因此某些功能可能无法使用</string>\n    <string name=\"no_min_version_specified\">该模块未指定所需的 Xposed 版本</string>\n    <string name=\"warning_min_version_too_low\">由于该模块开发时所基于 Xposed %1$d 版本不再兼容 %2$d 版本中的变更，该模块现已被停用</string>\n    <string name=\"warning_installed_on_external_storage\">此模块因被安装在 SD 卡中而导致无法加载，请将其移动到内部存储</string>\n    <string name=\"module_uninstall\">卸载</string>\n    <string name=\"module_settings\">模块设置</string>\n    <string name=\"view_in_repo\">在仓库中查看</string>\n    <string name=\"module_uninstall_message\">确认卸载该模块？</string>\n    <string name=\"module_uninstalled\">已卸载 %1$s</string>\n    <string name=\"module_uninstall_failed\">卸载失败</string>\n    <string name=\"add_module_to_user\">安装模块到用户</string>\n    <string name=\"module_installed\">已安装 %1$s 到用户 %2$s</string>\n    <string name=\"module_install_failed\">安装失败</string>\n    <string name=\"install_to_user\">安装到用户 %s</string>\n    <string name=\"install_to_user_message\">确认安装 %1$s 到用户 %2$s？推荐手动用系统自带方法安装，通过 LSPosed 强制安装可能会导致未知异常。</string>\n    <string name=\"expand\">展开</string>\n    <string name=\"collapse\">收起</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">重新优化</string>\n    <string name=\"compile_speed_msg\">优化中…</string>\n    <string name=\"compile_done\">优化完成</string>\n    <string name=\"app_launch\">启动</string>\n    <string name=\"compile_failed\">优化失败：返回值为空</string>\n    <string name=\"compile_failed_with_info\">优化失败：</string>\n    <string name=\"sort_by_name\">应用名</string>\n    <string name=\"sort_by_package_name\">包名</string>\n    <string name=\"sort_by_install_time\">安装时间</string>\n    <string name=\"sort_by_update_time\">更新时间</string>\n    <string name=\"sort_reverse\">倒序</string>\n    <string name=\"menu_show_system_apps\">系统应用</string>\n    <string name=\"menu_sort\">排序</string>\n    <string name=\"enable_module\">启用模块</string>\n    <string name=\"no_scope_selected\">未选择任何应用。继续？</string>\n    <string name=\"menu_show_games\">游戏</string>\n    <string name=\"menu_show_modules\">模块</string>\n    <string name=\"menu_show_denylist\">排除列表</string>\n    <string name=\"failed_to_save_scope_list\">作用域列表保存失败</string>\n    <string name=\"app_version\">版本：%1$s</string>\n    <string name=\"menu_select\">选择</string>\n    <string name=\"use_recommended\">勾选推荐</string>\n    <string name=\"no_scope_selected_has_recommended\">未选择任何应用。选择推荐的应用？</string>\n    <string name=\"use_recommended_message\">选择推荐的应用？</string>\n    <string name=\"menu_select_all\">全部</string>\n    <string name=\"menu_select_none\">空</string>\n    <string name=\"menu_auto_include\">自动添加</string>\n    <string name=\"module_is_not_activated_yet\">Xposed 模块尚未激活</string>\n    <string name=\"requested_by_module\">推荐应用</string>\n    <string name=\"update_available\">可用更新：%1$s</string>\n    <string name=\"module_disabled_no_selection\">由于未选择任何应用，模块 %s 已被禁用。</string>\n    <string name=\"android_framework\">系统框架</string>\n    <string name=\"menu_backup_and_restore\">备份</string>\n    <string name=\"menu_backup\">备份</string>\n    <string name=\"menu_restore\">恢复</string>\n    <string name=\"force_stop\">强行停止</string>\n    <string name=\"force_stop_dlg_title\">要强行停止吗？</string>\n    <string name=\"force_stop_dlg_text\">强行停止某个应用可能会使其异常。</string>\n    <string name=\"reboot_required\">重启以应用此更改</string>\n    <string name=\"reboot\">重启系统</string>\n    <string name=\"menu_hide\">隐藏</string>\n    <string name=\"deny_list\">%s 在排除列表内。模块可能会不生效</string>\n    <string name=\"deny_list_info\">在排除列表内</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">在其它应用中查看</string>\n    <string name=\"module_app_info\">应用信息</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\n空空如也</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">框架</string>\n    <string name=\"settings_disable_verbose_log\">禁用详细日志</string>\n    <string name=\"settings_disable_verbose_log_summary\">报告问题要求包含详细日志</string>\n    <string name=\"settings_enable_log_watchdog\">启用日志监控</string>\n    <string name=\"settings_enable_log_watchdog_summary\">LSPosed 的日志监视修改了系统属性，可以被利用来检测 LSPosed</string>\n    <string name=\"pure_black_dark_theme\">纯黑主题</string>\n    <string name=\"pure_black_dark_theme_summary\">当深色主题启用时使用纯黑主题</string>\n    <string name=\"settings_group_theme\">主题</string>\n    <string name=\"settings_backup_and_restore\">备份与恢复</string>\n    <string name=\"settings_backup_summery\">备份模块列表与作用域列表</string>\n    <string name=\"settings_restore_summery\">恢复模块列表与作用域列表</string>\n    <string name=\"settings_backup\">备份</string>\n    <string name=\"settings_backup_failed2\">备份失败：\\n%s</string>\n    <string name=\"enable_documentui\">请启用文档应用</string>\n    <string name=\"settings_restore\">恢复</string>\n    <string name=\"settings_restore_failed2\">恢复失败：\\n%s</string>\n    <string name=\"group_network\">网络</string>\n    <string name=\"dns_over_http\">安全 DNS（DoH）</string>\n    <string name=\"dns_over_http_summary\">解决某些地区的 DNS 污染问题</string>\n    <string name=\"theme_color\">主题颜色</string>\n    <string name=\"theme_color_system\">系统主题色</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">强制显示桌面图标</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">在 Android 10 或更高版本，应用不再允许隐藏桌面图标。关闭该选项以关闭该系统功能。</string>\n    <string name=\"settings_group_system\">系统</string>\n    <string name=\"settings_language\">语言</string>\n    <string name=\"settings_translation_contributors\">译者</string>\n    <string name=\"settings_translation\">参与翻译</string>\n    <string name=\"settings_translation_summary\">帮助我们把 %s 翻译到你的语言</string>\n    <string name=\"settings_create_shortcut_summary\">创建一个能打开寄生管理器的快捷方式</string>\n    <string name=\"settings_shortcut_pinned_hint\">已创建快捷方式</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">当前默认桌面不支持固定快捷方式</string>\n    <string name=\"settings_enable_status_notification\">状态通知</string>\n    <string name=\"settings_enable_status_notification_summary\">显示一个通知以打开寄生管理器</string>\n    <string name=\"disable_status_notification_error\">没有快捷方式，无法禁用通知</string>\n    <string name=\"settings_update_channel\">模块更新通道</string>\n    <string name=\"update_channel_stable\">稳定版</string>\n    <string name=\"update_channel_bate\">测试版</string>\n    <string name=\"update_channel_nightly\">每夜版</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API 调用保护</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">阻止模块动态加载的代码使用 Xposed API，这会使某些模块失效，但有利于安全性</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">自述文件</string>\n    <string name=\"module_releases\">版本</string>\n    <string name=\"module_information\">信息</string>\n    <string name=\"module_information_homepage\">主页</string>\n    <string name=\"module_information_source_url\">源码</string>\n    <string name=\"module_information_collaborators\">协作者</string>\n    <string name=\"module_release_view_assets\">附件</string>\n    <string name=\"menu_open_in_browser\">在浏览器中打开</string>\n    <string name=\"module_release_load_more\">显示较早版本</string>\n    <string name=\"module_release_no_more\">无更多版本</string>\n    <string name=\"repo_load_failed\">模块仓库加载失败：%s</string>\n    <string name=\"sort_upgradable_first\">可更新优先</string>\n    <string name=\"installed\">已安装</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d 次下载</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">樱花</string>\n    <string name=\"color_red\">红色</string>\n    <string name=\"color_pink\">粉色</string>\n    <string name=\"color_purple\">紫色</string>\n    <string name=\"color_deep_purple\">深紫</string>\n    <string name=\"color_indigo\">靛青</string>\n    <string name=\"color_blue\">蓝色</string>\n    <string name=\"color_light_blue\">浅蓝</string>\n    <string name=\"color_cyan\">青色</string>\n    <string name=\"color_teal\">青绿</string>\n    <string name=\"color_green\">绿色</string>\n    <string name=\"color_light_green\">浅绿</string>\n    <string name=\"color_lime\">黄绿</string>\n    <string name=\"color_yellow\">黄色</string>\n    <string name=\"color_amber\">琥珀</string>\n    <string name=\"color_orange\">橙色</string>\n    <string name=\"color_deep_orange\">深橙</string>\n    <string name=\"color_brown\">棕色</string>\n    <string name=\"color_blue_grey\">灰蓝</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rHK/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">概觀</string>\n    <string name=\"Modules\">模組</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">%d 個模組已啟用</item>\n    </plurals>\n    <string name=\"Logs\">記錄</string>\n    <string name=\"Settings\">設定</string>\n    <string name=\"feedback_or_suggestion\">回饋或建議</string>\n    <string name=\"About\">關於</string>\n    <string name=\"report_issue\">回報問題</string>\n    <string name=\"module_repo\">資料庫</string>\n    <string name=\"module_repo_up_to_date\">所有模組為最新版本</string>\n    <string name=\"module_repo_published_time\">發佈於 %s</string>\n    <string name=\"module_repo_updated_time\">更新於 %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d 個模組可更新</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[在 %1$s 檢視原始碼<br/>加入我們的 %2$s 頻道]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">DarKnighT0v0</string>\n    <string name=\"install\">安裝</string>\n    <string name=\"install_summary\">點選安裝 LSPosed</string>\n    <string name=\"not_installed\">未安裝</string>\n    <string name=\"not_install_summary\">LSPosed 未安裝</string>\n    <string name=\"activated\">已啟用</string>\n    <string name=\"partial_activated\">部分啟用</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy 未被正確讀取</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[部分功能將無法運作。<br/>請將此回報給 <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> 開發人員。]]></string>\n    <string name=\"system_inject_fail_summary\">系統架構插入失敗</string>\n    <string name=\"system_inject_fail\"><![CDATA[這是極罕見的情況，可能是由 <b>Magisk</b> 或低品質 Magisk 模組導致，<br/>請嘗試停用除 Riru 和 LSPosed 外的 Magisk 模組，或向開發人員提供完整記錄。]]></string>\n    <string name=\"system_prop_incorrect_summary\">系統屬性異常</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[部分必須的系統屬性被刪除或被修改，<br/>模組可能會隨機失效。]]></string>\n    <string name=\"need_update\">需要更新</string>\n    <string name=\"please_update_summary\">請安裝最新版本的 LSPosed</string>\n    <string name=\"developer_warning_title\">給模組開發人員的提示</string>\n    <string name=\"developer_warning_summary\">請在 Android Studio 上停用部署最佳化，或使用 `gradlew installDebug` 指令進行安裝。否則模組apk將不會更新。</string>\n    <string name=\"info_api_version\">API版本</string>\n    <string name=\"info_framework_version\">架構版本</string>\n    <string name=\"info_manager_package_name\">管理器包名</string>\n    <string name=\"info_system_version\">系統版本</string>\n    <string name=\"info_device\">裝置版本</string>\n    <string name=\"info_system_abi\">系統架構</string>\n    <string name=\"info_dex2oat_wrapper\">Dex 最佳化包裝函式</string>\n    <string name=\"enabled\">已啟用</string>\n    <string name=\"not_enabled\">未啟用</string>\n    <string name=\"supported\">支援</string>\n    <string name=\"unsupported\">不支援</string>\n    <string name=\"android_version_unsatisfied\">系統版本不受支援</string>\n    <string name=\"crashed\">崩潰</string>\n    <string name=\"mount_failed\">加載失敗</string>\n    <string name=\"selinux_permissive\">SELinux 處於寬容模式</string>\n    <string name=\"sepolicy_incorrect\">SELinux 原則異常</string>\n    <string name=\"update_lsposed\">更新 LSPosed</string>\n    <string name=\"update_lsposed_msg\">確認更新LSPosed? 此裝置會於更新完成後重啟</string>\n    <string name=\"info_copied\">已複製到剪貼簿</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">歡迎使用 LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">您正在使用寄生管理員，您可以建立捷徑或從通知開啟。</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">您正在使用寄生管理員，它可以從通知中開啟。</string>\n    <string name=\"create_shortcut\">建立捷徑</string>\n    <string name=\"never_show\">不再顯示</string>\n    <string name=\"app_welcome\">建議使用寄生管理員</string>\n    <string name=\"app_welcome_summary\">LSPosed 現在支援系統寄生以避免偵測，您可以從通知開啟寄生管理員。建議解除安裝目前應用程式。</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">儲存</string>\n    <string name=\"nav_item_logs_verbose\">詳細記錄</string>\n    <string name=\"nav_item_logs_module\">模組記錄</string>\n    <string name=\"logs_saving\">正在保存日誌，請稍候</string>\n    <string name=\"logs_saved\">記錄已儲存</string>\n    <string name=\"logs_save_failed2\">儲存失敗：\\n%s</string>\n    <string name=\"menuClearLog\">立即清理記錄</string>\n    <string name=\"logs_cleared\">記錄清理成功</string>\n    <string name=\"scroll_top\">移至頂端</string>\n    <string name=\"loading\">正在載入…</string>\n    <string name=\"scroll_bottom\">移至底端</string>\n    <string name=\"menuReload\">重新載入</string>\n    <string name=\"logs_clear_failed_2\">記錄清理失敗</string>\n    <string name=\"menu_enable_word_wrap\">自動換行</string>\n    <string name=\"enabled_verbose_log\">詳細記錄已啟用</string>\n    <string name=\"disabled_verbose_log\">詳細記錄已停用</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(未提供描述)</string>\n    <string name=\"warning_xposed_min_version\">此模組需要更新版本的 Xposed 版本 (%d)，因此無法被啟用</string>\n    <string name=\"warning_target_version_higher\">此模組專為較新的 Xposed 版本 (%d) 而設計，因此某些功能可能無法運作</string>\n    <string name=\"no_min_version_specified\">該模組未指定需要的 Xposed 版本</string>\n    <string name=\"warning_min_version_too_low\">此模組適用於 %1$d 版本的 Xposed ，由於版本 %2$d 的變更不相容，因此已經停用此模組</string>\n    <string name=\"warning_installed_on_external_storage\">由於此模組被安裝在SD卡中而無法載入，請將其移動到內部儲存空間</string>\n    <string name=\"module_uninstall\">解除安裝</string>\n    <string name=\"module_settings\">模組設定</string>\n    <string name=\"view_in_repo\">在存放庫中檢視</string>\n    <string name=\"module_uninstall_message\">您確定要移除此模組嗎？</string>\n    <string name=\"module_uninstalled\">已移除 %1$s</string>\n    <string name=\"module_uninstall_failed\">移除失敗</string>\n    <string name=\"add_module_to_user\">為用戶安裝模組</string>\n    <string name=\"module_installed\">已為用戶 %2$s 安裝模組 %1$s</string>\n    <string name=\"module_install_failed\">模組安裝失敗</string>\n    <string name=\"install_to_user\">為用戶 %s 安裝</string>\n    <string name=\"install_to_user_message\">確定要為用戶 %2$s 安裝 %1$s 嗎？建議手動安裝或多開，透過 LSPosed 強制安裝可能會出現問題。</string>\n    <string name=\"expand\">展開</string>\n    <string name=\"collapse\">收起</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">重新最佳化</string>\n    <string name=\"compile_speed_msg\">正在最佳化…</string>\n    <string name=\"compile_done\">最佳化完成</string>\n    <string name=\"app_launch\">執行</string>\n    <string name=\"compile_failed\">最佳化失敗或返回值為空</string>\n    <string name=\"compile_failed_with_info\">最佳化失敗： </string>\n    <string name=\"sort_by_name\">應用程式名稱</string>\n    <string name=\"sort_by_package_name\">套件名稱</string>\n    <string name=\"sort_by_install_time\">安裝時間</string>\n    <string name=\"sort_by_update_time\">更新時間</string>\n    <string name=\"sort_reverse\">遞減</string>\n    <string name=\"menu_show_system_apps\">系統應用程式</string>\n    <string name=\"menu_sort\">排序</string>\n    <string name=\"enable_module\">啟用模組</string>\n    <string name=\"no_scope_selected\">未選擇任何應用程式，是否繼續？</string>\n    <string name=\"menu_show_games\">遊戲</string>\n    <string name=\"menu_show_modules\">模組</string>\n    <string name=\"menu_show_denylist\">Magisk 排除清單</string>\n    <string name=\"failed_to_save_scope_list\">作用範圍清單儲存失敗</string>\n    <string name=\"app_version\">版本：%1$s</string>\n    <string name=\"menu_select\">選擇</string>\n    <string name=\"use_recommended\">推薦應用程式</string>\n    <string name=\"no_scope_selected_has_recommended\">未選擇任何應用程式，選擇推薦的應用程式？</string>\n    <string name=\"use_recommended_message\">選擇推薦的應用程式?</string>\n    <string name=\"menu_select_all\">全部</string>\n    <string name=\"menu_select_none\">空</string>\n    <string name=\"menu_auto_include\">自動添加</string>\n    <string name=\"module_is_not_activated_yet\">Xposed 模組尚未啟用</string>\n    <string name=\"requested_by_module\">推薦應用程式</string>\n    <string name=\"update_available\">可用更新：%1$s</string>\n    <string name=\"module_disabled_no_selection\">由於未選擇任何應用程式，模組 %s 已被停用。</string>\n    <string name=\"android_framework\">系統架構</string>\n    <string name=\"menu_backup_and_restore\">備份</string>\n    <string name=\"menu_backup\">備份</string>\n    <string name=\"menu_restore\">還原</string>\n    <string name=\"force_stop\">強制停止</string>\n    <string name=\"force_stop_dlg_title\">確定要強制停止？</string>\n    <string name=\"force_stop_dlg_text\">如果您強制停止應用程式，可能會導致行為異常。</string>\n    <string name=\"reboot_required\">重啟以套用變更</string>\n    <string name=\"reboot\">重啟系統</string>\n    <string name=\"menu_hide\">隱藏</string>\n    <string name=\"deny_list\">%s 在排除清單中，模組可能無法生效。</string>\n    <string name=\"deny_list_info\">在排除清單內</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">在其他應用程式中檢視</string>\n    <string name=\"module_app_info\">應用程式資訊</string>\n    <string name=\"list_empty\">¯\\\\\\\\_(ツ)_\\/¯\\n這裡甚麼都沒有</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">框架</string>\n    <string name=\"settings_disable_verbose_log\">禁用詳細紀錄檔</string>\n    <string name=\"settings_disable_verbose_log_summary\">回報問題要求包含詳細記錄檔</string>\n    <string name=\"settings_enable_log_watchdog\">啟用日誌監控</string>\n    <string name=\"settings_enable_log_watchdog_summary\">LSPosed 的日誌監控會修改系統屬性，可能被利用來偵測 LSPosed</string>\n    <string name=\"pure_black_dark_theme\">使用純黑深色主題</string>\n    <string name=\"pure_black_dark_theme_summary\">使用純黑色背景當深色模式已啟用</string>\n    <string name=\"settings_group_theme\">主題</string>\n    <string name=\"settings_backup_and_restore\">備份與還原</string>\n    <string name=\"settings_backup_summery\">備份模組清單和作用範圍清單</string>\n    <string name=\"settings_restore_summery\">還原模組清單和作用範圍清單</string>\n    <string name=\"settings_backup\">備份</string>\n    <string name=\"settings_backup_failed2\">備份失敗：\\n%s</string>\n    <string name=\"enable_documentui\">請啟用 DocumentUI</string>\n    <string name=\"settings_restore\">還原</string>\n    <string name=\"settings_restore_failed2\">還原失敗：\\n%s</string>\n    <string name=\"group_network\">網絡</string>\n    <string name=\"dns_over_http\">安全 DNS（DoH）</string>\n    <string name=\"dns_over_http_summary\">解決部分地區的 DNS 中毒問題</string>\n    <string name=\"theme_color\">主題色彩</string>\n    <string name=\"theme_color_system\">系統主題色彩</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">強制應用程式在啟動器中顯示圖示</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">Android 10之後不允許隱藏桌面圖示。關閉開關以禁用此功能</string>\n    <string name=\"settings_group_system\">系統</string>\n    <string name=\"settings_language\">語言</string>\n    <string name=\"settings_translation_contributors\">譯者</string>\n    <string name=\"settings_translation\">參與翻譯</string>\n    <string name=\"settings_translation_summary\">幫助我們翻譯 %s 到您的語言</string>\n    <string name=\"settings_create_shortcut_summary\">建立可以開啟寄生管理員的捷徑</string>\n    <string name=\"settings_shortcut_pinned_hint\">捷徑已釘選</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">目前的預設啟動器不支援釘選捷徑</string>\n    <string name=\"settings_enable_status_notification\">狀態通知</string>\n    <string name=\"settings_enable_status_notification_summary\">顯示通知以便開啟寄生管理員</string>\n    <string name=\"disable_status_notification_error\">沒有捷徑，無法停用通知</string>\n    <string name=\"settings_update_channel\">更新頻道</string>\n    <string name=\"update_channel_stable\">穩定版</string>\n    <string name=\"update_channel_bate\">測試版</string>\n    <string name=\"update_channel_nightly\">每夜構建</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API 呼叫保護</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">封鎖動態載入的模組代碼以使用 Xposed API，這可能會損毀某些模組但有利於安全性</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">自述文件</string>\n    <string name=\"module_releases\">版本</string>\n    <string name=\"module_information\">資訊</string>\n    <string name=\"module_information_homepage\">首頁</string>\n    <string name=\"module_information_source_url\">原始程式碼</string>\n    <string name=\"module_information_collaborators\">合作者</string>\n    <string name=\"module_release_view_assets\">附件</string>\n    <string name=\"menu_open_in_browser\">在瀏覽器中打開</string>\n    <string name=\"module_release_load_more\">顯示較舊版本</string>\n    <string name=\"module_release_no_more\">沒有更舊版本</string>\n    <string name=\"repo_load_failed\">模組存放庫載入失敗：%s</string>\n    <string name=\"sort_upgradable_first\">可更新優先</string>\n    <string name=\"installed\">已安裝</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d 次下載</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">櫻花</string>\n    <string name=\"color_red\">紅色</string>\n    <string name=\"color_pink\">粉色</string>\n    <string name=\"color_purple\">紫色</string>\n    <string name=\"color_deep_purple\">深紫</string>\n    <string name=\"color_indigo\">靛青</string>\n    <string name=\"color_blue\">藍色</string>\n    <string name=\"color_light_blue\">淺藍</string>\n    <string name=\"color_cyan\">青色</string>\n    <string name=\"color_teal\">青綠</string>\n    <string name=\"color_green\">綠色</string>\n    <string name=\"color_light_green\">淺綠</string>\n    <string name=\"color_lime\">萊姆綠</string>\n    <string name=\"color_yellow\">黃色</string>\n    <string name=\"color_amber\">琥珀</string>\n    <string name=\"color_orange\">橙色</string>\n    <string name=\"color_deep_orange\">深橙</string>\n    <string name=\"color_brown\">棕色</string>\n    <string name=\"color_blue_grey\">藍灰</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:keep=\"@string/xposed_module_updated_notification_*,@string/module_is_not_activated_*\">\n    <!-- MainActivity -->\n    <string name=\"overview\">概觀</string>\n    <string name=\"Modules\">模組</string>\n    <plurals name=\"modules_enabled_count\">\n        <item quantity=\"other\">%d 個模組已啟用</item>\n    </plurals>\n    <string name=\"Logs\">日誌</string>\n    <string name=\"Settings\">設定</string>\n    <string name=\"feedback_or_suggestion\">回饋或建議</string>\n    <string name=\"About\">關於</string>\n    <string name=\"report_issue\">回報問題</string>\n    <string name=\"module_repo\">倉庫</string>\n    <string name=\"module_repo_up_to_date\">所有模組為最新版本</string>\n    <string name=\"module_repo_published_time\">發佈於 %s</string>\n    <string name=\"module_repo_updated_time\">更新於 %s</string>\n    <plurals name=\"module_repo_upgradable\">\n        <item quantity=\"other\">%d 個模組可更新</item>\n    </plurals>\n    <string name=\"about_view_source_code\"><![CDATA[在 %1$s 檢視原始碼<br/>加入我們的 %2$s 頻道]]></string>\n    <string name=\"translators\" comment=\"Dear translators, please add your home page here following the format. There can be more than one translator. So feel free to append yourself.\">孟武. 尼德霍格. 龍、david082321、beigua87</string>\n    <string name=\"install\">安裝</string>\n    <string name=\"install_summary\">點選安裝 LSPosed</string>\n    <string name=\"not_installed\">未安裝</string>\n    <string name=\"not_install_summary\">LSPosed 未安裝</string>\n    <string name=\"activated\">已啟用</string>\n    <string name=\"partial_activated\">部分啟用</string>\n    <string name=\"selinux_policy_not_loaded_summary\">SEPolicy 未被正確讀取</string>\n    <string name=\"selinux_policy_not_loaded\"><![CDATA[部分功能將無法運作。<br/>請將此回報給 <a href=\"https://github.com/topjohnwu/Magisk/issues\"><b>Magisk</b></a> 開發人員。]]></string>\n    <string name=\"system_inject_fail_summary\">系統框架注入失敗</string>\n    <string name=\"system_inject_fail\"><![CDATA[這是極罕見的情況，可能是由 <b>Magisk</b> 或低品質 Magisk 模組導致，<br/>請嘗試停用除 Riru 和 LSPosed 外的 Magisk 模組，或向開發者提供完整日誌。]]></string>\n    <string name=\"system_prop_incorrect_summary\">系統屬性異常</string>\n    <string name=\"system_prop_incorrect\"><![CDATA[部分必須的系統屬性被刪除或被修改，<br/>模組可能會隨機失效。]]></string>\n    <string name=\"need_update\">需要更新</string>\n    <string name=\"please_update_summary\">請安裝最新版本的 LSPosed</string>\n    <string name=\"developer_warning_title\">給模組開發人員的提示</string>\n    <string name=\"developer_warning_summary\">請在 Android Studio 上停用部署最佳化，或使用 `gradlew installDebug` 指令進行安裝。否則模組apk將不會更新。</string>\n    <string name=\"info_api_version\">API 版本</string>\n    <string name=\"info_framework_version\">框架版本</string>\n    <string name=\"info_manager_package_name\">管理器包名</string>\n    <string name=\"info_system_version\">系統版本</string>\n    <string name=\"info_device\">裝置版本</string>\n    <string name=\"info_system_abi\">系統架構</string>\n    <string name=\"info_dex2oat_wrapper\">Dex 優化器包裝器</string>\n    <string name=\"enabled\">已啟用</string>\n    <string name=\"not_enabled\">未啟用</string>\n    <string name=\"supported\">支援</string>\n    <string name=\"unsupported\">不支援</string>\n    <string name=\"android_version_unsatisfied\">系統版本不受支援</string>\n    <string name=\"crashed\">當機</string>\n    <string name=\"mount_failed\">掛載失敗</string>\n    <string name=\"selinux_permissive\">SELinux 處於寬容模式</string>\n    <string name=\"sepolicy_incorrect\">SELinux 規則異常</string>\n    <string name=\"update_lsposed\">更新 LSPosed</string>\n    <string name=\"update_lsposed_msg\">確定要更新LSPosed嗎？更新完成後將會重啟裝置</string>\n    <string name=\"info_copied\">已複製到剪貼簿</string>\n    <!-- WelcomeDialog -->\n    <string name=\"parasitic_welcome\">歡迎使用 LSPosed</string>\n    <string name=\"parasitic_welcome_summary\">您正在使用寄生管理員，您可以建立捷徑或從通知開啟。</string>\n    <string name=\"parasitic_welcome_summary_no_shortcut_support\">您正在使用寄生管理員，它可以從通知中開啟。</string>\n    <string name=\"create_shortcut\">建立捷徑</string>\n    <string name=\"never_show\">不再顯示</string>\n    <string name=\"app_welcome\">建議使用寄生管理員</string>\n    <string name=\"app_welcome_summary\">LSPosed 現在支援系統寄生以避免偵測，您可以從通知開啟寄生管理員。建議解除安裝目前應用程式。</string>\n    <!-- LogsActivity -->\n    <string name=\"menuSaveToSd\">儲存</string>\n    <string name=\"nav_item_logs_verbose\">詳細日誌</string>\n    <string name=\"nav_item_logs_module\">模組日誌</string>\n    <string name=\"logs_saving\">正在保存日誌，請稍候</string>\n    <string name=\"logs_saved\">日誌已儲存</string>\n    <string name=\"logs_save_failed2\">儲存失敗：\\n%s</string>\n    <string name=\"menuClearLog\">立即清理日誌</string>\n    <string name=\"logs_cleared\">日誌清理成功</string>\n    <string name=\"scroll_top\">移至頂端</string>\n    <string name=\"loading\">正在載入……</string>\n    <string name=\"scroll_bottom\">移至底端</string>\n    <string name=\"menuReload\">重新載入</string>\n    <string name=\"logs_clear_failed_2\">日誌清理失敗</string>\n    <string name=\"menu_enable_word_wrap\">自動換行</string>\n    <string name=\"enabled_verbose_log\">詳細日誌已啟用</string>\n    <string name=\"disabled_verbose_log\">詳細日誌已禁用</string>\n    <!-- ModulesActivity -->\n    <string name=\"module_empty_description\">(未提供介紹)</string>\n    <string name=\"warning_xposed_min_version\">該模組需要更新版本的 Xposed(%d)，因此無法被啟用</string>\n    <string name=\"warning_target_version_higher\">此模組專為較新的 Xposed 版本 (%d) 而設計，因此某些功能可能無法運作</string>\n    <string name=\"no_min_version_specified\">該模組未指定需要的 Xposed 版本</string>\n    <string name=\"warning_min_version_too_low\">此模組適用於 %1$d 版本的 Xposed ，由於版本 %2$d 的變更不相容，因此已經停用此模組</string>\n    <string name=\"warning_installed_on_external_storage\">由於此模組被安裝在SD卡中而無法載入，請將其移動到內部儲存空間</string>\n    <string name=\"module_uninstall\">解除安裝</string>\n    <string name=\"module_settings\">模組設定</string>\n    <string name=\"view_in_repo\">在倉庫中檢視</string>\n    <string name=\"module_uninstall_message\">您確定要移除此模組嗎？</string>\n    <string name=\"module_uninstalled\">已移除 %1$s</string>\n    <string name=\"module_uninstall_failed\">移除失敗</string>\n    <string name=\"add_module_to_user\">為使用者安裝模組</string>\n    <string name=\"module_installed\">已為使用者 %2$s 安裝模組 %1$s</string>\n    <string name=\"module_install_failed\">模組安裝失敗</string>\n    <string name=\"install_to_user\">為使用者 %s 安裝</string>\n    <string name=\"install_to_user_message\">確定要為使用者 %2$s 安裝 %1$s 嗎？建議手動安裝或多開，透過 LSPosed 強制安裝可能會出現問題。</string>\n    <string name=\"expand\">展開</string>\n    <string name=\"collapse\">收起</string>\n    <!-- AppListActivity -->\n    <string name=\"compile_speed\">重新最佳化</string>\n    <string name=\"compile_speed_msg\">正在最佳化……</string>\n    <string name=\"compile_done\">最佳化完成</string>\n    <string name=\"app_launch\">執行</string>\n    <string name=\"compile_failed\">最佳化失敗或返回值為空</string>\n    <string name=\"compile_failed_with_info\">最佳化失敗： </string>\n    <string name=\"sort_by_name\">程式名稱</string>\n    <string name=\"sort_by_package_name\">套件名稱</string>\n    <string name=\"sort_by_install_time\">安裝時間</string>\n    <string name=\"sort_by_update_time\">更新時間</string>\n    <string name=\"sort_reverse\">遞減</string>\n    <string name=\"menu_show_system_apps\">系統程式</string>\n    <string name=\"menu_sort\">排序</string>\n    <string name=\"enable_module\">啟用模組</string>\n    <string name=\"no_scope_selected\">未選擇任何程式，是否繼續？</string>\n    <string name=\"menu_show_games\">遊戲</string>\n    <string name=\"menu_show_modules\">模組</string>\n    <string name=\"menu_show_denylist\">Magisk 排除列表</string>\n    <string name=\"failed_to_save_scope_list\">作用域列表儲存失敗</string>\n    <string name=\"app_version\">版本：%1$s</string>\n    <string name=\"use_recommended\">推薦程式</string>\n    <string name=\"no_scope_selected_has_recommended\">未選擇任何程式，選擇推薦的程式？</string>\n    <string name=\"use_recommended_message\">選擇推薦的程式？</string>\n    <string name=\"module_is_not_activated_yet\">Xposed 模組尚未啟用</string>\n    <string name=\"requested_by_module\">推薦啟用</string>\n    <string name=\"update_available\">可用更新：%1$s</string>\n    <string name=\"module_disabled_no_selection\">由於未選擇任何程式，模組 %s 已被停用。</string>\n    <string name=\"android_framework\">系統框架</string>\n    <string name=\"menu_backup_and_restore\">備份</string>\n    <string name=\"menu_backup\">備份</string>\n    <string name=\"menu_restore\">還原</string>\n    <string name=\"force_stop\">強制停止</string>\n    <string name=\"force_stop_dlg_title\">確定要強制停止？</string>\n    <string name=\"force_stop_dlg_text\">如果您強制停止應用程式，可能導致行為異常。</string>\n    <string name=\"reboot_required\">需要重新啟動以套用此變更</string>\n    <string name=\"reboot\">重啟系統</string>\n    <string name=\"menu_hide\">隱藏</string>\n    <string name=\"deny_list\">%s 在排除列表中，模組可能無法生效。</string>\n    <string name=\"deny_list_info\">在排除列表內</string>\n    <!-- ModulesActivity and AppListActivity -->\n    <string name=\"modules_other_app\">在其他應用程式中檢視</string>\n    <string name=\"module_app_info\">程式資訊</string>\n    <string name=\"list_empty\">¯\\_(ツ)_/¯\\n空空如也</string>\n    <!-- SettingsActivity -->\n    <string name=\"settings_group_framework\">框架</string>\n    <string name=\"settings_disable_verbose_log\">停用詳細日誌</string>\n    <string name=\"settings_disable_verbose_log_summary\">回報問題要求包含詳細日誌</string>\n    <string name=\"settings_enable_log_watchdog\">啟用日誌看門狗</string>\n    <string name=\"settings_enable_log_watchdog_summary\">LSPosed 的日誌看門狗會修改系統屬性，可被利用來偵測 LSPosed</string>\n    <string name=\"pure_black_dark_theme\">黑色主題</string>\n    <string name=\"pure_black_dark_theme_summary\">當深色主題啟用時使用純黑色主題</string>\n    <string name=\"settings_group_theme\">主題</string>\n    <string name=\"settings_backup_and_restore\">備份與還原</string>\n    <string name=\"settings_backup_summery\">備份模組列表和作用域清單</string>\n    <string name=\"settings_restore_summery\">還原模組列表和作用域清單</string>\n    <string name=\"settings_backup\">備份</string>\n    <string name=\"settings_backup_failed2\">備份失敗：\\n%s</string>\n    <string name=\"enable_documentui\">請啟用 DocumentUI</string>\n    <string name=\"settings_restore\">還原</string>\n    <string name=\"settings_restore_failed2\">還原失敗：\\n%s</string>\n    <string name=\"group_network\">網路</string>\n    <string name=\"dns_over_http\">安全 DNS（DoH）</string>\n    <string name=\"dns_over_http_summary\">解決部分地區的 DNS 中毒問題</string>\n    <string name=\"theme_color\">主題強調色</string>\n    <string name=\"theme_color_system\">系統主題顏色</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled\">強制應用程式在啟動器中顯示圖示</string>\n    <string name=\"settings_show_hidden_icon_apps_enabled_summary\">在 Android 10 之後，應用（特別是 Xposed 模組）不被允許隱藏啟動器圖示。關閉本選項以停用此功能。</string>\n    <string name=\"settings_group_system\">系統</string>\n    <string name=\"settings_language\">語言</string>\n    <string name=\"settings_translation_contributors\">譯者</string>\n    <string name=\"settings_translation\">參與翻譯</string>\n    <string name=\"settings_translation_summary\">幫助我們翻譯 %s 到您的語言</string>\n    <string name=\"settings_create_shortcut_summary\">建立可以開啟寄生管理員的捷徑</string>\n    <string name=\"settings_shortcut_pinned_hint\">捷徑已釘選</string>\n    <string name=\"settings_unsupported_pin_shortcut_summary\">目前的預設啟動器不支援釘選捷徑</string>\n    <string name=\"settings_enable_status_notification\">狀態通知</string>\n    <string name=\"settings_enable_status_notification_summary\">顯示通知以便開啟寄生管理員</string>\n    <string name=\"disable_status_notification_error\">沒有捷徑，無法停用通知</string>\n    <string name=\"settings_update_channel\">更新通道</string>\n    <string name=\"update_channel_stable\">穩定版</string>\n    <string name=\"update_channel_bate\">測試版</string>\n    <string name=\"update_channel_nightly\">每夜構建</string>\n    <string name=\"settings_xposed_api_call_protection\">Xposed API 呼叫保護</string>\n    <string name=\"settings_xposed_api_call_protection_summary\">封鎖動態載入的模組代碼以使用 Xposed API，這可能會損毀某些模組但有益於安全性</string>\n    <!-- Module Repo -->\n    <string name=\"module_readme\">自述檔案</string>\n    <string name=\"module_releases\">版本</string>\n    <string name=\"module_information\">資訊</string>\n    <string name=\"module_information_homepage\">首頁</string>\n    <string name=\"module_information_source_url\">原始碼</string>\n    <string name=\"module_information_collaborators\">合作者</string>\n    <string name=\"module_release_view_assets\">附件</string>\n    <string name=\"menu_open_in_browser\">在瀏覽器中開啟</string>\n    <string name=\"module_release_load_more\">顯示較舊的版本</string>\n    <string name=\"module_release_no_more\">沒有更舊的版本</string>\n    <string name=\"repo_load_failed\">模組倉庫載入失敗：%s</string>\n    <string name=\"sort_upgradable_first\">可更新的優先</string>\n    <string name=\"installed\">已安裝</string>\n    <plurals name=\"module_release_assets_download_count\">\n        <item quantity=\"other\">%d 次下載</item>\n    </plurals>\n    <!-- Colors -->\n    <string name=\"color_sakura\">櫻花</string>\n    <string name=\"color_red\">紅色</string>\n    <string name=\"color_pink\">粉色</string>\n    <string name=\"color_purple\">紫色</string>\n    <string name=\"color_deep_purple\">深紫</string>\n    <string name=\"color_indigo\">靛青</string>\n    <string name=\"color_blue\">藍色</string>\n    <string name=\"color_light_blue\">淺藍</string>\n    <string name=\"color_cyan\">青色</string>\n    <string name=\"color_teal\">青綠</string>\n    <string name=\"color_green\">綠色</string>\n    <string name=\"color_light_green\">淺綠</string>\n    <string name=\"color_lime\">萊姆綠</string>\n    <string name=\"color_yellow\">黃色</string>\n    <string name=\"color_amber\">琥珀</string>\n    <string name=\"color_orange\">橙色</string>\n    <string name=\"color_deep_orange\">深橙</string>\n    <string name=\"color_brown\">棕色</string>\n    <string name=\"color_blue_grey\">藍灰</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/prefs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <PreferenceCategory android:title=\"@string/group_network\">\n\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"false\"\n            android:icon=\"@drawable/ic_outline_dns_24\"\n            android:key=\"doh\"\n            android:summary=\"@string/dns_over_http_summary\"\n            android:title=\"@string/dns_over_http\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/settings_language\">\n\n        <rikka.preference.SimpleMenuPreference\n            android:defaultValue=\"SYSTEM\"\n            android:icon=\"@drawable/ic_outline_language_24\"\n            android:key=\"language\"\n            android:popupTheme=\"@style/language_menu_style\"\n            android:summary=\"%s\"\n            android:title=\"@string/settings_language\" />\n\n        <Preference\n            android:icon=\"@drawable/ic_outline_groups_24\"\n            android:key=\"translation_contributors\"\n            android:title=\"@string/settings_translation_contributors\"\n            app:enableCopying=\"true\" />\n\n        <Preference\n            android:icon=\"@drawable/ic_outline_translate_24\"\n            android:key=\"translation\"\n            android:title=\"@string/settings_translation\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/settings_group_theme\">\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"true\"\n            android:icon=\"@drawable/ic_outline_palette_24\"\n            android:key=\"follow_system_accent\"\n            android:title=\"@string/theme_color_system\"\n            app:isPreferenceVisible=\"false\" />\n        <rikka.preference.SimpleMenuPreference\n            android:defaultValue=\"COLOR_BLUE\"\n            android:entries=\"@array/color_texts\"\n            android:entryValues=\"@array/color_values\"\n            android:icon=\"@drawable/ic_outline_format_color_fill_24\"\n            android:key=\"theme_color\"\n            android:summary=\"%s\"\n            android:title=\"@string/theme_color\" />\n        <rikka.preference.SimpleMenuPreference\n            android:defaultValue=\"MODE_NIGHT_FOLLOW_SYSTEM\"\n            android:entries=\"@array/theme_texts\"\n            android:entryValues=\"@array/theme_values\"\n            android:icon=\"@drawable/ic_outline_dark_mode_24\"\n            android:key=\"dark_theme\"\n            android:summary=\"%s\"\n            android:title=\"@string/dark_theme\" />\n        <rikka.material.preference.MaterialSwitchPreference\n            android:icon=\"@drawable/ic_outline_invert_colors_24\"\n            android:key=\"black_dark_theme\"\n            android:summary=\"@string/pure_black_dark_theme_summary\"\n            android:title=\"@string/pure_black_dark_theme\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/settings_group_framework\">\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"false\"\n            android:icon=\"@drawable/ic_outline_assignment_24\"\n            android:key=\"disable_verbose_log\"\n            android:persistent=\"false\"\n            android:summary=\"@string/settings_disable_verbose_log_summary\"\n            android:title=\"@string/settings_disable_verbose_log\" />\n\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"true\"\n            android:icon=\"@drawable/ic_outline_assignment_24\"\n            android:key=\"enable_log_watchdog\"\n            android:persistent=\"false\"\n            android:summary=\"@string/settings_enable_log_watchdog_summary\"\n            android:title=\"@string/settings_enable_log_watchdog\" />\n\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"true\"\n            android:icon=\"@drawable/ic_outline_shield_24\"\n            android:key=\"enable_dex_obfuscate\"\n            android:persistent=\"false\"\n            android:summary=\"@string/settings_xposed_api_call_protection_summary\"\n            android:title=\"@string/settings_xposed_api_call_protection\" />\n\n        <Preference\n            android:icon=\"@drawable/ic_outline_app_shortcut_24\"\n            android:key=\"add_shortcut\"\n            android:summary=\"@string/settings_create_shortcut_summary\"\n            android:title=\"@string/create_shortcut\" />\n\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"false\"\n            android:icon=\"@drawable/ic_outline_speaker_notes_24\"\n            android:key=\"enable_status_notification\"\n            android:persistent=\"false\"\n            android:shouldDisableView=\"false\"\n            android:summary=\"@string/settings_enable_status_notification_summary\"\n            android:title=\"@string/settings_enable_status_notification\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/settings_group_repo\">\n        <rikka.preference.SimpleMenuPreference\n            android:defaultValue=\"CHANNEL_STABLE\"\n            android:entries=\"@array/update_channel_texts\"\n            android:entryValues=\"@array/update_channel_values\"\n            android:icon=\"@drawable/ic_outline_merge_type_24\"\n            android:key=\"update_channel\"\n            android:summary=\"%s\"\n            android:title=\"@string/settings_update_channel\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/settings_backup_and_restore\">\n        <Preference\n            android:icon=\"@drawable/ic_baseline_settings_backup_restore_24\"\n            android:key=\"backup\"\n            android:summary=\"@string/settings_backup_summery\"\n            android:title=\"@string/settings_backup\" />\n        <Preference\n            android:icon=\"@drawable/ic_outline_restore_24\"\n            android:key=\"restore\"\n            android:summary=\"@string/settings_restore_summery\"\n            android:title=\"@string/settings_restore\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:key=\"settings_group_system\"\n        android:title=\"@string/settings_group_system\"\n        app:isPreferenceVisible=\"@bool/show_system_settings\">\n        <rikka.material.preference.MaterialSwitchPreference\n            android:defaultValue=\"true\"\n            android:enabled=\"false\"\n            android:icon=\"@drawable/ic_outline_android_24\"\n            android:key=\"show_hidden_icon_apps_enabled\"\n            android:persistent=\"false\"\n            android:summary=\"@string/settings_show_hidden_icon_apps_enabled_summary\"\n            android:title=\"@string/settings_show_hidden_icon_apps_enabled\" />\n    </PreferenceCategory>\n</PreferenceScreen>\n"
  },
  {
    "path": "app/src/main/res/xml/shortcuts.xml",
    "content": "<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2020 EdXposed Contributors\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<shortcuts xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <shortcut\n        android:enabled=\"true\"\n        android:icon=\"@drawable/shortcut_ic_modules\"\n        android:shortcutId=\"modules\"\n        android:shortcutLongLabel=\"@string/Modules\"\n        android:shortcutShortLabel=\"@string/Modules\">\n        <intent\n            android:action=\"android.intent.action.MAIN\"\n            android:data=\"modules\"\n            android:targetClass=\"org.lsposed.manager.ui.activity.MainActivity\"\n            android:targetPackage=\"org.lsposed.manager\" />\n    </shortcut>\n    <shortcut\n        android:enabled=\"true\"\n        android:icon=\"@drawable/shortcut_ic_logs\"\n        android:shortcutId=\"logs\"\n        android:shortcutLongLabel=\"@string/Logs\"\n        android:shortcutShortLabel=\"@string/Logs\">\n        <intent\n            android:action=\"android.intent.action.MAIN\"\n            android:data=\"logs\"\n            android:targetClass=\"org.lsposed.manager.ui.activity.MainActivity\"\n            android:targetPackage=\"org.lsposed.manager\" />\n    </shortcut>\n    <shortcut\n        android:enabled=\"true\"\n        android:icon=\"@drawable/shortcut_ic_repo\"\n        android:shortcutId=\"repo\"\n        android:shortcutLongLabel=\"@string/module_repo\"\n        android:shortcutShortLabel=\"@string/module_repo\">\n        <intent\n            android:action=\"android.intent.action.MAIN\"\n            android:data=\"repo\"\n            android:targetClass=\"org.lsposed.manager.ui.activity.MainActivity\"\n            android:targetPackage=\"org.lsposed.manager\" />\n    </shortcut>\n    <shortcut\n        android:enabled=\"true\"\n        android:icon=\"@drawable/shortcut_ic_settings\"\n        android:shortcutId=\"settings\"\n        android:shortcutLongLabel=\"@string/Settings\"\n        android:shortcutShortLabel=\"@string/Settings\">\n        <intent\n            android:action=\"android.intent.action.MAIN\"\n            android:data=\"settings\"\n            android:targetClass=\"org.lsposed.manager.ui.activity.MainActivity\"\n            android:targetPackage=\"org.lsposed.manager\" />\n    </shortcut>\n</shortcuts>\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import com.android.build.api.dsl.ApplicationDefaultConfig\nimport com.android.build.api.dsl.CommonExtension\nimport com.android.build.gradle.api.AndroidBasePlugin\nimport com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask\nimport java.io.ByteArrayOutputStream\nimport javax.inject.Inject\nimport org.gradle.api.provider.ValueSource\nimport org.gradle.api.provider.ValueSourceParameters\nimport org.gradle.process.ExecOperations\n\nplugins {\n    alias(libs.plugins.agp.lib) apply false\n    alias(libs.plugins.agp.app) apply false\n    alias(libs.plugins.kotlin) apply false\n    alias(libs.plugins.ktfmt)\n}\n\n/** A ValueSource that executes 'git rev-list --count' to get the total commit count. */\nabstract class GitCommitCountValueSource : ValueSource<String, ValueSourceParameters.None> {\n    @get:Inject abstract val execOperations: ExecOperations\n\n    override fun obtain(): String {\n        val output = ByteArrayOutputStream()\n        val result =\n            execOperations.exec {\n                commandLine(\"git\", \"rev-list\", \"--count\", \"refs/remotes/origin/master\")\n                standardOutput = output\n                isIgnoreExitValue = true\n            }\n        // Return the count if successful, otherwise a default of \"1\".\n        return if (result.exitValue == 0 && output.toString().isNotBlank()) {\n            output.toString().trim()\n        } else {\n            \"1\"\n        }\n    }\n}\n\n/** A ValueSource that executes 'git tag' to get the latest version tag. */\nabstract class GitLatestTagValueSource : ValueSource<String, ValueSourceParameters.None> {\n    @get:Inject abstract val execOperations: ExecOperations\n\n    override fun obtain(): String {\n        val output = ByteArrayOutputStream()\n        val result =\n            execOperations.exec {\n                commandLine(\"git\", \"tag\", \"--list\", \"--sort=-v:refname\")\n                standardOutput = output\n                isIgnoreExitValue = true\n            }\n        // If successful, parse the first line. Provide a default if no tags are found.\n        return if (result.exitValue == 0 && output.toString().isNotBlank()) {\n            output.toString().lineSequence().first().removePrefix(\"v\")\n        } else {\n            \"1.0\"\n        }\n    }\n}\n\n// This defers the execution of the git commands and allows Gradle to cache the results.\nval versionCodeProvider by extra(providers.of(GitCommitCountValueSource::class.java) {})\nval versionNameProvider by extra(providers.of(GitLatestTagValueSource::class.java) {})\n\nval injectedPackageName by extra(\"com.android.shell\")\nval injectedPackageUid by extra(2000)\nval defaultManagerPackageName by extra(\"org.lsposed.manager\")\n\nval androidTargetSdkVersion by extra(36)\nval androidMinSdkVersion by extra(27)\nval androidBuildToolsVersion by extra(\"36.0.0\")\nval androidCompileSdkVersion by extra(36)\nval androidCompileNdkVersion by extra(\"29.0.13113456\")\nval androidSourceCompatibility by extra(JavaVersion.VERSION_21)\nval androidTargetCompatibility by extra(JavaVersion.VERSION_21)\n\nsubprojects {\n    plugins.withType(AndroidBasePlugin::class.java) {\n        extensions.configure(CommonExtension::class.java) {\n            compileSdk = androidCompileSdkVersion\n            ndkVersion = androidCompileNdkVersion\n            buildToolsVersion = androidBuildToolsVersion\n\n            buildFeatures { buildConfig = true }\n            externalNativeBuild {\n                cmake {\n                    version = \"3.29.8+\"\n                    buildStagingDirectory = layout.buildDirectory.get().asFile\n                }\n            }\n\n            defaultConfig {\n                minSdk = androidMinSdkVersion\n                ndk { abiFilters.addAll(listOf(\"arm64-v8a\", \"armeabi-v7a\", \"x86\", \"x86_64\")) }\n\n                if (this is ApplicationDefaultConfig) {\n                    targetSdk = androidTargetSdkVersion\n\n                    versionCode = versionCodeProvider.get().toInt()\n                    versionName = versionNameProvider.get()\n                }\n\n                val flags =\n                    listOf(\n                        \"-DVERSION_CODE=${versionCodeProvider.get()}\",\n                        \"-DVERSION_NAME='\\\"${versionNameProvider.get()}\\\"'\",\n                    )\n\n                val args =\n                    listOf(\n                        \"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON\",\n                        \"-DVECTOR_ROOT=${rootDir.absolutePath}\",\n                    )\n\n                externalNativeBuild {\n                    cmake {\n                        cFlags.addAll(flags)\n                        cppFlags.addAll(flags)\n                        arguments.addAll(args)\n                    }\n                }\n            }\n\n            buildTypes {\n                getByName(\"release\") {\n                    externalNativeBuild {\n                        cmake {\n                            arguments.add(\n                                \"-DDEBUG_SYMBOLS_PATH=${\n                                layout.buildDirectory.dir(\"symbols\").get().asFile.absolutePath\n                            }\"\n                            )\n                        }\n                    }\n                }\n            }\n\n            lint {\n                abortOnError = true\n                checkReleaseBuilds = false\n            }\n\n            compileOptions {\n                sourceCompatibility = androidSourceCompatibility\n                targetCompatibility = androidTargetCompatibility\n            }\n        }\n    }\n    plugins.withType(JavaPlugin::class.java) {\n        extensions.configure(JavaPluginExtension::class.java) {\n            sourceCompatibility = androidSourceCompatibility\n            targetCompatibility = androidTargetCompatibility\n        }\n    }\n}\n\ntasks.register<KtfmtFormatTask>(\"format\") {\n    source = project.fileTree(rootDir)\n    include(\n        \"*.gradle.kts\",\n        \"*/build.gradle.kts\",\n        \"hiddenapi/*/build.gradle.kts\",\n        \"services/*-service/build.gradle.kts\",\n    )\n    dependsOn(\":xposed:ktfmtFormat\")\n    dependsOn(\":zygisk:ktfmtFormat\")\n}\n\nktfmt { kotlinLangStyle() }\n"
  },
  {
    "path": "core/.gitignore",
    "content": "/build\n/.cxx\n/src/main/jni/src/config.cpp\n"
  },
  {
    "path": "core/build.gradle.kts",
    "content": "val versionCodeProvider: Provider<String> by rootProject.extra\nval versionNameProvider: Provider<String> by rootProject.extra\n\nplugins { alias(libs.plugins.agp.lib) }\n\nandroid {\n    namespace = \"org.lsposed.lspd.core\"\n\n    androidResources { enable = false }\n\n    defaultConfig {\n        consumerProguardFiles(\"proguard-rules.pro\")\n        buildConfigField(\"String\", \"FRAMEWORK_NAME\", \"\"\"\"${rootProject.name}\"\"\"\")\n        buildConfigField(\"String\", \"VERSION_NAME\", \"\"\"\"${versionCodeProvider.get()}\"\"\"\")\n        buildConfigField(\"long\", \"VERSION_CODE\", versionCodeProvider.get())\n    }\n\n    buildTypes {\n        release {\n            isMinifyEnabled = true\n            proguardFiles(\"proguard-rules.pro\")\n        }\n    }\n}\n\ndependencies {\n    api(projects.xposed)\n    implementation(projects.external.apache)\n    implementation(projects.external.axml)\n    implementation(projects.hiddenapi.bridge)\n    implementation(projects.services.daemonService)\n    implementation(projects.services.managerService)\n    compileOnly(libs.androidx.annotation)\n    compileOnly(projects.hiddenapi.stubs)\n}\n"
  },
  {
    "path": "core/proguard-rules.pro",
    "content": "-keep class android.** { *; }\n-keep class de.robv.android.xposed.** {*;}\n-keep class io.github.libxposed.** {*;}\n-keep class org.lsposed.lspd.core.* {*;}\n-keep class org.lsposed.lspd.hooker.HandleSystemServerProcessHooker {*;}\n-keep class org.lsposed.lspd.hooker.HandleSystemServerProcessHooker$Callback {*;}\n-keep class org.lsposed.lspd.impl.LSPosedBridge$NativeHooker {*;}\n-keep class org.lsposed.lspd.impl.LSPosedBridge$HookerCallback {*;}\n-keep class org.lsposed.lspd.util.Hookers {*;}\n\n-keepnames class org.lsposed.lspd.impl.LSPosedHelper {\n    public <methods>;\n}\n\n-keepattributes RuntimeVisibleAnnotations\n-keepclasseswithmembers,includedescriptorclasses class * {\n    native <methods>;\n}\n-keepclassmembers class org.lsposed.lspd.impl.LSPosedContext {\n    public <methods>;\n}\n-keepclassmembers class org.lsposed.lspd.impl.LSPosedHookCallback {\n    public <methods>;\n}\n-keepclassmembers,allowoptimization class ** implements io.github.libxposed.api.XposedInterface$Hooker {\n    public static *** before();\n    public static *** before(io.github.libxposed.api.XposedInterface$BeforeHookCallback);\n    public static void after();\n    public static void after(io.github.libxposed.api.XposedInterface$AfterHookCallback);\n    public static void after(io.github.libxposed.api.XposedInterface$AfterHookCallback, ***);\n}\n-assumenosideeffects class android.util.Log {\n    public static *** v(...);\n    public static *** d(...);\n}\n-repackageclasses\n-allowaccessmodification\n"
  },
  {
    "path": "core/src/main/java/android/app/AndroidAppHelper.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.app;\n\nimport static de.robv.android.xposed.XposedHelpers.findClass;\nimport static de.robv.android.xposed.XposedHelpers.findFieldIfExists;\nimport static de.robv.android.xposed.XposedHelpers.findMethodExactIfExists;\nimport static de.robv.android.xposed.XposedHelpers.getObjectField;\nimport static de.robv.android.xposed.XposedHelpers.newInstance;\nimport static de.robv.android.xposed.XposedHelpers.setFloatField;\n\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.CompatibilityInfo;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.view.Display;\n\nimport java.lang.ref.WeakReference;\nimport java.util.Map;\n\nimport de.robv.android.xposed.XSharedPreferences;\nimport de.robv.android.xposed.XposedBridge;\n\n/**\n * Contains various methods for information about the current app.\n *\n * <p>For historical reasons, this class is in the {@code android.app} package. It can't be moved\n * without breaking compatibility with existing modules.\n */\npublic final class AndroidAppHelper {\n\tprivate AndroidAppHelper() {}\n\n\tprivate static final Class<?> CLASS_RESOURCES_KEY;\n\tprivate static final boolean HAS_IS_THEMEABLE;\n\tprivate static final boolean HAS_THEME_CONFIG_PARAMETER;\n\n\tstatic {\n\t\tCLASS_RESOURCES_KEY = findClass(\"android.content.res.ResourcesKey\", null);\n\n\t\tHAS_IS_THEMEABLE = findFieldIfExists(CLASS_RESOURCES_KEY, \"mIsThemeable\") != null;\n\t\tHAS_THEME_CONFIG_PARAMETER = HAS_IS_THEMEABLE && findMethodExactIfExists(\"android.app.ResourcesManager\", null, \"getThemeConfig\") != null;\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate static Map<Object, WeakReference> getResourcesMap(ActivityThread activityThread) {\n\t\tObject resourcesManager = getObjectField(activityThread, \"mResourcesManager\");\n\t\treturn (Map) getObjectField(resourcesManager, \"mResourceImpls\");\n\t}\n\n\t/* For SDK 24+ */\n\tprivate static Object createResourcesKey(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo) {\n\t\ttry {\n\t\t\treturn newInstance(CLASS_RESOURCES_KEY, resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfiguration, compatInfo);\n\t\t} catch (Throwable t) {\n\t\t\tXposedBridge.log(t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** @hide */\n\tpublic static void addActiveResource(String resDir, float scale, boolean isThemeable, Resources resources) {\n\t\taddActiveResource(resDir, resources);\n\t}\n\n\t/** @hide */\n\tpublic static void addActiveResource(String resDir, Resources resources) {\n\t\tActivityThread thread = ActivityThread.currentActivityThread();\n\t\tif (thread == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tObject resourcesKey;\n\t\tCompatibilityInfo compatInfo = (CompatibilityInfo) newInstance(CompatibilityInfo.class);\n\t\tsetFloatField(compatInfo, \"applicationScale\", resources.hashCode());\n\t\tresourcesKey = createResourcesKey(resDir, null, null, null, Display.DEFAULT_DISPLAY, null, compatInfo);\n\n\t\tif (resourcesKey != null) {\n\t\t\tObject resImpl = getObjectField(resources, \"mResourcesImpl\");\n\t\t\tgetResourcesMap(thread).put(resourcesKey, new WeakReference<>(resImpl));\n\t\t}\n\t}\n\n\t/**\n\t * Returns the name of the current process. It's usually the same as the main package name.\n\t */\n\tpublic static String currentProcessName() {\n\t\tString processName = ActivityThread.currentPackageName();\n\t\tif (processName == null)\n\t\t\treturn \"android\";\n\t\treturn processName;\n\t}\n\n\t/**\n\t * Returns information about the main application in the current process.\n\t *\n\t * <p>In a few cases, multiple apps might run in the same process, e.g. the SystemUI and the\n\t * Keyguard which both have {@code android:process=\"com.android.systemui\"} set in their\n\t * manifest. In those cases, the first application that was initialized will be returned.\n\t */\n\tpublic static ApplicationInfo currentApplicationInfo() {\n\t\tActivityThread am = ActivityThread.currentActivityThread();\n\t\tif (am == null)\n\t\t\treturn null;\n\n\t\tObject boundApplication = getObjectField(am, \"mBoundApplication\");\n\t\tif (boundApplication == null)\n\t\t\treturn null;\n\n\t\treturn (ApplicationInfo) getObjectField(boundApplication, \"appInfo\");\n\t}\n\n\t/**\n\t * Returns the Android package name of the main application in the current process.\n\t *\n\t * <p>In a few cases, multiple apps might run in the same process, e.g. the SystemUI and the\n\t * Keyguard which both have {@code android:process=\"com.android.systemui\"} set in their\n\t * manifest. In those cases, the first application that was initialized will be returned.\n\t */\n\tpublic static String currentPackageName() {\n\t\tApplicationInfo ai = currentApplicationInfo();\n\t\treturn (ai != null) ? ai.packageName : \"android\";\n\t}\n\n\t/**\n\t * Returns the main {@link android.app.Application} object in the current process.\n\t *\n\t * <p>In a few cases, multiple apps might run in the same process, e.g. the SystemUI and the\n\t * Keyguard which both have {@code android:process=\"com.android.systemui\"} set in their\n\t * manifest. In those cases, the first application that was initialized will be returned.\n\t */\n\tpublic static Application currentApplication() {\n\t\treturn ActivityThread.currentApplication();\n\t}\n\n\t/** @deprecated Use {@link XSharedPreferences} instead. */\n\t@SuppressWarnings(\"UnusedParameters\")\n\t@Deprecated\n\tpublic static SharedPreferences getSharedPreferencesForPackage(String packageName, String prefFileName, int mode) {\n\t\treturn new XSharedPreferences(packageName, prefFileName);\n\t}\n\n\t/** @deprecated Use {@link XSharedPreferences} instead. */\n\t@Deprecated\n\tpublic static SharedPreferences getDefaultSharedPreferencesForPackage(String packageName) {\n\t\treturn new XSharedPreferences(packageName);\n\t}\n\n\t/** @deprecated Use {@link XSharedPreferences#reload} instead. */\n\t@Deprecated\n\tpublic static void reloadSharedPreferencesIfNeeded(SharedPreferences pref) {\n\t\tif (pref instanceof XSharedPreferences) {\n\t\t\t((XSharedPreferences) pref).reload();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/android/content/res/XModuleResources.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.content.res;\n\nimport android.app.AndroidAppHelper;\nimport android.util.DisplayMetrics;\n\nimport de.robv.android.xposed.IXposedHookInitPackageResources;\nimport de.robv.android.xposed.IXposedHookZygoteInit;\nimport de.robv.android.xposed.IXposedHookZygoteInit.StartupParam;\nimport de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;\nimport hidden.HiddenApiBridge;\n\n/**\n * Provides access to resources from a certain path (usually the module's own path).\n */\npublic class XModuleResources extends Resources {\n\tprivate XModuleResources(AssetManager assets, DisplayMetrics metrics, Configuration config) {\n\t\tsuper(assets, metrics, config);\n\t}\n\n\t/**\n\t * Creates a new instance.\n\t *\n\t * <p>This is usually called with {@link StartupParam#modulePath} from\n\t * {@link IXposedHookZygoteInit#initZygote} and {@link InitPackageResourcesParam#res} from\n\t * {@link IXposedHookInitPackageResources#handleInitPackageResources} (or {@code null} for\n\t * system-wide replacements).\n\t *\n\t * @param path The path to the APK from which the resources should be loaded.\n\t * @param origRes The resources object from which settings like the display metrics and the\n\t *                configuration should be copied. May be {@code null}.\n\t */\n\tpublic static XModuleResources createInstance(String path, XResources origRes) {\n\t\tif (path == null)\n\t\t\tthrow new IllegalArgumentException(\"path must not be null\");\n\n\t\tAssetManager assets = new AssetManager();\n\t\tHiddenApiBridge.AssetManager_addAssetPath(assets, path);\n\n\t\tXModuleResources res;\n\t\tif (origRes != null)\n\t\t\tres = new XModuleResources(assets, origRes.getDisplayMetrics(),\torigRes.getConfiguration());\n\t\telse\n\t\t\tres = new XModuleResources(assets, null, null);\n\n\t\tAndroidAppHelper.addActiveResource(path, res);\n\t\treturn res;\n\t}\n\n\t/**\n\t * Creates an {@link XResForwarder} instance that forwards requests to {@code id} in this resource.\n\t */\n\tpublic XResForwarder fwd(int id) {\n\t\treturn new XResForwarder(this, id);\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/android/content/res/XResForwarder.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.content.res;\n\n/**\n * Instances of this class can be used for {@link XResources#setReplacement(String, String, String, Object)}\n * and its variants. They forward the resource request to a different {@link android.content.res.Resources}\n * instance with a possibly different ID.\n *\n * <p>Usually, instances aren't created directly but via {@link XModuleResources#fwd}.\n */\npublic class XResForwarder {\n\tprivate final Resources res;\n\tprivate final int id;\n\n\t/**\n\t * Creates a new instance.\n\t *\n\t * @param res The target {@link android.content.res.Resources} instance to forward requests to.\n\t * @param id The target resource ID.\n\t */\n\tpublic XResForwarder(Resources res, int id) {\n\t\tthis.res = res;\n\t\tthis.id = id;\n\t}\n\n\t/** Returns the target {@link android.content.res.Resources} instance. */\n\tpublic Resources getResources() {\n\t\treturn res;\n\t}\n\n\t/** Returns the target resource ID. */\n\tpublic int getId() {\n\t\treturn id;\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/android/content/res/XResources.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.content.res;\n\nimport static org.matrix.vector.nativebridge.ResourcesHook.rewriteXmlReferencesNative;\nimport static de.robv.android.xposed.XposedHelpers.decrementMethodDepth;\nimport static de.robv.android.xposed.XposedHelpers.findAndHookMethod;\nimport static de.robv.android.xposed.XposedHelpers.getBooleanField;\nimport static de.robv.android.xposed.XposedHelpers.getLongField;\nimport static de.robv.android.xposed.XposedHelpers.getObjectField;\nimport static de.robv.android.xposed.XposedHelpers.incrementMethodDepth;\n\nimport android.content.Context;\nimport android.content.pm.PackageParser;\nimport android.content.pm.PackageParser.PackageParserException;\nimport android.graphics.Color;\nimport android.graphics.Movie;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.os.StrictMode;\nimport android.text.Html;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\nimport android.util.SparseArray;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresApi;\n\nimport org.xmlpull.v1.XmlPullParser;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.WeakHashMap;\n\nimport de.robv.android.xposed.IXposedHookZygoteInit;\nimport de.robv.android.xposed.XC_MethodHook;\nimport de.robv.android.xposed.XC_MethodHook.MethodHookParam;\nimport de.robv.android.xposed.XposedBridge;\nimport de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;\nimport de.robv.android.xposed.XposedInit;\nimport de.robv.android.xposed.callbacks.XC_LayoutInflated;\nimport de.robv.android.xposed.callbacks.XC_LayoutInflated.LayoutInflatedParam;\nimport de.robv.android.xposed.callbacks.XCallback;\nimport xposed.dummy.XResourcesSuperClass;\nimport xposed.dummy.XTypedArraySuperClass;\n\n/**\n * {@link android.content.res.Resources} subclass that allows replacing individual resources.\n *\n * <p>Xposed replaces the standard resources with this class, which overrides the methods used for\n * retrieving individual resources and adds possibilities to replace them. These replacements can\n * be set using the methods made available via the API methods in this class.\n */\n@SuppressWarnings(\"JniMissingFunction\")\npublic class XResources extends XResourcesSuperClass {\n\tprivate static final SparseArray<HashMap<String, Object>> sReplacements = new SparseArray<>();\n\tprivate static final SparseArray<HashMap<String, ResourceNames>> sResourceNames = new SparseArray<>();\n\n\t// A resource ID is a 32 bit number of the form: PPTTNNNN. PP is the package the resource is for;\n\t// TT is the type of the resource;\n\t// NNNN is the name of the resource in that type.\n\t// For applications resources, PP is always 0x7f.\n\tprivate static final byte[] sSystemReplacementsCache = new byte[256]; // bitmask: 0x000700ff => 2048 bit => 256 bytes\n\tprivate byte[] mReplacementsCache; // bitmask: 0x0007007f => 1024 bit => 128 bytes\n\tprivate static final HashMap<String, byte[]> sReplacementsCacheMap = new HashMap<>();\n\tprivate static final SparseArray<ColorStateList> sColorStateListCache = new SparseArray<>(0);\n\n\tprivate static final SparseArray<HashMap<String, CopyOnWriteSortedSet<XC_LayoutInflated>>> sLayoutCallbacks = new SparseArray<>();\n\tprivate static final WeakHashMap<XmlResourceParser, XMLInstanceDetails> sXmlInstanceDetails = new WeakHashMap<>();\n\n\tprivate static final String EXTRA_XML_INSTANCE_DETAILS = \"xmlInstanceDetails\";\n\tprivate static final ThreadLocal<LinkedList<MethodHookParam>> sIncludedLayouts = ThreadLocal.withInitial(() -> new LinkedList<>());\n\n\tprivate static final HashMap<String, Long> sResDirLastModified = new HashMap<>();\n\tprivate static final HashMap<String, String> sResDirPackageNames = new HashMap<>();\n\tprivate static ThreadLocal<Object> sLatestResKey = null;\n\n\tprivate String mResDir;\n\tprivate String mPackageName;\n\n\tpublic XResources(ClassLoader classLoader, String resDir) {\n\t\tsuper(classLoader);\n\n\t\tthis.mResDir = resDir;\n\t\tthis.mPackageName = getPackageName(resDir);\n\n\t\tif (resDir != null) {\n\t\t\tsynchronized (sReplacementsCacheMap) {\n\t\t\t\tmReplacementsCache = sReplacementsCacheMap.computeIfAbsent(resDir, k -> new byte[128]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Dummy, will never be called (objects are transferred to this class only). */\n//\tprivate XResources() {\n//\t\tthrow new UnsupportedOperationException();\n//\t}\n\n\t/** @hide */\n\tpublic boolean isFirstLoad() {\n\t\tsynchronized (sReplacements) {\n\t\t\tif (mResDir == null)\n\t\t\t\treturn false;\n\n\t\t\tfinal StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();\n\t\t\tLong lastModification = new File(mResDir).lastModified();\n\t\t\tLong oldModified = sResDirLastModified.get(mResDir);\n\t\t\tStrictMode.setThreadPolicy(policy);\n\t\t\tif (lastModification.equals(oldModified))\n\t\t\t\treturn false;\n\n\t\t\tsResDirLastModified.put(mResDir, lastModification);\n\n\t\t\tif (oldModified == null)\n\t\t\t\treturn true;\n\n\t\t\t// file was changed meanwhile => remove old replacements\n\t\t\tfor (int i = 0; i < sReplacements.size(); i++) {\n\t\t\t\tsReplacements.valueAt(i).remove(mResDir);\n\t\t\t}\n\t\t\tArrays.fill(mReplacementsCache, (byte) 0);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/** @hide */\n\tpublic static void setPackageNameForResDir(String packageName, String resDir) {\n\t\tsynchronized (sResDirPackageNames) {\n\t\t\tsResDirPackageNames.put(resDir, packageName);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the name of the package that these resources belong to, or \"android\" for system resources.\n\t */\n\t@NonNull\n\tpublic String getPackageName() {\n\t\treturn mPackageName;\n\t}\n\n\tprivate static String getPackageName(String resDir) {\n\t\tif (resDir == null)\n\t\t\treturn \"android\";\n\n\t\tString packageName;\n\t\tsynchronized (sResDirPackageNames) {\n\t\t\tpackageName = sResDirPackageNames.get(resDir);\n\t\t}\n\n\t\tif (packageName != null)\n\t\t\treturn packageName;\n\n\t\tPackageParser.PackageLite pkgInfo;\n\t\ttry {\n\t\t\tpkgInfo = PackageParser.parsePackageLite(new File(resDir), 0);\n\t\t} catch (PackageParserException e) {\n\t\t\tthrow new IllegalStateException(\"Could not determine package name for \" + resDir, e);\n\t\t}\n\t\tif (pkgInfo != null && pkgInfo.packageName != null) {\n//\t\t\tLog.w(XposedBridge.TAG, \"Package name for \" + resDir + \" had to be retrieved via parser\");\n\t\t\tpackageName = pkgInfo.packageName;\n\t\t\tsetPackageNameForResDir(packageName, resDir);\n\t\t\treturn packageName;\n\t\t}\n\n\t\tthrow new IllegalStateException(\"Could not determine package name for \" + resDir);\n\t}\n\n\t/**\n\t * Special case of {@link #getPackageName} during object creation.\n\t *\n\t * <p>For a short moment during/after the creation of a new {@link android.content.res Resources}\n\t * object, it isn't an instance of {@link XResources} yet. For any hooks that need information\n\t * about the just created object during this particular stage, this method will return the\n\t * package name.\n\t *\n\t * <p class=\"warning\">If you call this method outside of {@code getTopLevelResources()}, it\n\t * throws an {@code IllegalStateException}.\n\t */\n\tpublic static String getPackageNameDuringConstruction() {\n\t\tObject key;\n\t\tif (sLatestResKey == null || (key = sLatestResKey.get()) == null)\n\t\t\tthrow new IllegalStateException(\"This method can only be called during getTopLevelResources()\");\n\n\t\tString resDir = (String) getObjectField(key, \"mResDir\");\n\t\treturn getPackageName(resDir);\n\t}\n\n\t/** @hide */\n\tpublic static void init(ThreadLocal<Object> latestResKey) throws Exception {\n\t\tsLatestResKey = latestResKey;\n\n\t\tfindAndHookMethod(LayoutInflater.class, \"inflate\", XmlPullParser.class, ViewGroup.class, boolean.class, new XC_MethodHook() {\n\t\t\t@Override\n\t\t\tprotected void afterHookedMethod(MethodHookParam param) throws Throwable {\n\t\t\t\tif (param.hasThrowable())\n\t\t\t\t\treturn;\n\n\t\t\t\tXMLInstanceDetails details;\n\t\t\t\tsynchronized (sXmlInstanceDetails) {\n\t\t\t\t\tdetails = sXmlInstanceDetails.get(param.args[0]);\n\t\t\t\t}\n\t\t\t\tif (details != null) {\n\t\t\t\t\tLayoutInflatedParam liparam = new LayoutInflatedParam(details.callbacks);\n\t\t\t\t\tliparam.view = (View) param.getResult();\n\t\t\t\t\tliparam.resNames = details.resNames;\n\t\t\t\t\tliparam.variant = details.variant;\n\t\t\t\t\tliparam.res = details.res;\n\t\t\t\t\tXCallback.callAll(liparam);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tfinal XC_MethodHook parseIncludeHook = new XC_MethodHook() {\n\t\t\t@Override\n\t\t\tprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n\t\t\t\tsIncludedLayouts.get().push(param);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void afterHookedMethod(MethodHookParam param) throws Throwable {\n\t\t\t\tsIncludedLayouts.get().pop();\n\n\t\t\t\tif (param.hasThrowable())\n\t\t\t\t\treturn;\n\n\t\t\t\t// filled in by our implementation of getLayout()\n\t\t\t\tXMLInstanceDetails details = (XMLInstanceDetails) param.getObjectExtra(EXTRA_XML_INSTANCE_DETAILS);\n\t\t\t\tif (details != null) {\n\t\t\t\t\tLayoutInflatedParam liparam = new LayoutInflatedParam(details.callbacks);\n\t\t\t\t\tViewGroup group = (ViewGroup) param.args[2];\n\t\t\t\t\tliparam.view = group.getChildAt(group.getChildCount() - 1);\n\t\t\t\t\tliparam.resNames = details.resNames;\n\t\t\t\t\tliparam.variant = details.variant;\n\t\t\t\t\tliparam.res = details.res;\n\t\t\t\t\tXCallback.callAll(liparam);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tfindAndHookMethod(LayoutInflater.class, \"parseInclude\", XmlPullParser.class, Context.class,\n\t\t\t\tView.class, AttributeSet.class, parseIncludeHook);\n\t}\n\n\t/**\n\t * Wrapper for information about an indiviual resource.\n\t */\n\tpublic static class ResourceNames {\n\t\t/** The resource ID. */\n\t\tpublic final int id;\n\t\t/** The resource package name as returned by {@link #getResourcePackageName}. */\n\t\tpublic final String pkg;\n\t\t/** The resource entry name as returned by {@link #getResourceEntryName}. */\n\t\tpublic final String name;\n\t\t/** The resource type name as returned by {@link #getResourceTypeName}. */\n\t\tpublic final String type;\n\t\t/** The full resource nameas returned by {@link #getResourceName}. */\n\t\tpublic final String fullName;\n\n\t\tprivate ResourceNames(int id, String pkg, String name, String type) {\n\t\t\tthis.id = id;\n\t\t\tthis.pkg = pkg;\n\t\t\tthis.name = name;\n\t\t\tthis.type = type;\n\t\t\tthis.fullName = pkg + \":\" + type + \"/\" + name;\n\t\t}\n\n\t\t/**\n\t\t * Returns whether all non-null parameters match the values of this object.\n\t\t */\n\t\tpublic boolean equals(String pkg, String name, String type, int id) {\n\t\t\treturn (pkg  == null || pkg.equals(this.pkg))\n\t\t\t\t&& (name == null || name.equals(this.name))\n\t\t\t\t&& (type == null || type.equals(this.type))\n\t\t\t\t&& (id == 0 || id == this.id);\n\t\t}\n\t}\n\n\tprivate ResourceNames getResourceNames(int id) {\n\t\treturn new ResourceNames(\n\t\t\t\tid,\n\t\t\t\tgetResourcePackageName(id),\n\t\t\t\tgetResourceTypeName(id),\n\t\t\t\tgetResourceEntryName(id));\n\t}\n\n\tprivate static ResourceNames getSystemResourceNames(int id) {\n\t\tResources sysRes = getSystem();\n\t\treturn new ResourceNames(\n\t\t\t\tid,\n\t\t\t\tsysRes.getResourcePackageName(id),\n\t\t\t\tsysRes.getResourceTypeName(id),\n\t\t\t\tsysRes.getResourceEntryName(id));\n\t}\n\n\tprivate static void putResourceNames(String resDir, ResourceNames resNames) {\n\t\tint id = resNames.id;\n\t\tsynchronized (sResourceNames) {\n\t\t\tHashMap<String, ResourceNames> inner = sResourceNames.get(id);\n\t\t\tif (inner == null) {\n\t\t\t\tinner = new HashMap<>();\n\t\t\t\tsResourceNames.put(id, inner);\n\t\t\t}\n\t\t\tsynchronized (inner) {\n\t\t\t\tinner.put(resDir, resNames);\n\t\t\t}\n\t\t}\n\t}\n\n\t// =======================================================\n\t//   DEFINING REPLACEMENTS\n\t// =======================================================\n\n\t/**\n\t * Sets a replacement for an individual resource. See {@link #setReplacement(String, String, String, Object)}.\n\t *\n\t * @param id The ID of the resource which should be replaced.\n\t * @param replacement The replacement, see above.\n\t */\n\tpublic void setReplacement(int id, Object replacement) {\n\t\tsetReplacement(id, replacement, this);\n\t}\n\n\t/**\n\t * Sets a replacement for an individual resource. See {@link #setReplacement(String, String, String, Object)}.\n\t *\n\t * @deprecated Use {@link #setReplacement(String, String, String, Object)} instead.\n\t *\n\t * @param fullName The full resource name, e.g. {@code com.example.myapplication:string/app_name}.\n\t *                 See {@link #getResourceName}.\n\t * @param replacement The replacement.\n\t */\n\t@Deprecated\n\tpublic void setReplacement(String fullName, Object replacement) {\n\t\tint id = getIdentifier(fullName, null, null);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(fullName);\n\t\tsetReplacement(id, replacement, this);\n\t}\n\n\t/**\n\t * Sets a replacement for an individual resource. If called more than once for the same ID, the\n\t * replacement from the last call is used. Setting the replacement to {@code null} removes it.\n\t *\n\t * <p>The allowed replacements depend on the type of the source. All types accept an\n\t * {@link XResForwarder} object, which is usually created with {@link XModuleResources#fwd}.\n\t * The resource request will then be forwarded to another {@link android.content.res.Resources}\n\t * object. In addition to that, the following replacement types are accepted:\n\t *\n\t * <table>\n\t *     <thead>\n\t *     <tr><th>Resource type</th> <th>Additional allowed replacement types (*)</th> <th>Returned from (**)</th></tr>\n\t *     </thead>\n\t *\n\t *     <tbody>\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/animation-resource.html\">Animation</a></td>\n\t *         <td>&nbsp;<i>none</i></td>\n\t *         <td>{@link #getAnimation}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/more-resources.html#Bool\">Bool</a></td>\n\t *         <td>{@link Boolean}</td>\n\t *         <td>{@link #getBoolean}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/more-resources.html#Color\">Color</a></td>\n\t *         <td>{@link Integer} (you might want to use {@link Color#parseColor})</td>\n\t *         <td>{@link #getColor}<br>\n\t *             {@link #getDrawable} (creates a {@link ColorDrawable})<br>\n\t *             {@link #getColorStateList} (calls {@link android.content.res.ColorStateList#valueOf})\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/color-list-resource.html\">Color State List</a></td>\n\t *         <td>{@link android.content.res.ColorStateList}<br>\n\t *             {@link Integer} (calls {@link android.content.res.ColorStateList#valueOf})\n\t *         </td>\n\t *         <td>{@link #getColorStateList}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/more-resources.html#Dimension\">Dimension</a></td>\n\t *         <td>{@link DimensionReplacement} <i>(since v50)</i></td>\n\t *         <td>{@link #getDimension}<br>\n\t *             {@link #getDimensionPixelOffset}<br>\n\t *             {@link #getDimensionPixelSize}\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/drawable-resource.html\">Drawable</a>\n\t *             (including <a href=\"http://developer.android.com/tools/projects/index.html#mipmap\">mipmap</a>)</td>\n\t *         <td>{@link DrawableLoader}<br>\n\t *             {@link Integer} (creates a {@link ColorDrawable})\n\t *         </td>\n\t *         <td>{@link #getDrawable}<br>\n\t *             {@link #getDrawableForDensity}\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     <tr><td>Fraction</td>\n\t *         <td>&nbsp;<i>none</i></td>\n\t *         <td>{@link #getFraction}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/more-resources.html#Integer\">Integer</a></td>\n\t *         <td>{@link Integer}</td>\n\t *         <td>{@link #getInteger}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/more-resources.html#IntegerArray\">Integer Array</a></td>\n\t *         <td>{@code int[]}</td>\n\t *         <td>{@link #getIntArray}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/layout-resource.html\">Layout</a></td>\n\t *         <td>&nbsp;<i>none, but see {@link #hookLayout}</i></td>\n\t *         <td>{@link #getLayout}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td>Movie</td>\n\t *         <td>&nbsp;<i>none</i></td>\n\t *         <td>{@link #getMovie}</td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/string-resource.html#Plurals\">Quantity Strings (Plurals)</a></td>\n\t *         <td>&nbsp;<i>none</i></td>\n\t *         <td>{@link #getQuantityString}<br>\n\t *             {@link #getQuantityText}\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/string-resource.html#String\">String</a></td>\n\t *         <td>{@link String}<br>\n\t *             {@link CharSequence} (for styled texts, see also {@link Html#fromHtml})\n\t *         </td>\n\t *         <td>{@link #getString}<br>\n\t *             {@link #getText}\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     <tr><td><a href=\"http://developer.android.com/guide/topics/resources/string-resource.html#StringArray\">String Array</a></td>\n\t *         <td>{@code String[]}<br>\n\t *             {@code CharSequence[]} (for styled texts, see also {@link Html#fromHtml})\n\t *         </td>\n\t *         <td>{@link #getStringArray}<br>\n\t *             {@link #getTextArray}\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     <tr><td>XML</td>\n\t *         <td>&nbsp;<i>none</i></td>\n\t *         <td>{@link #getXml}<br>\n\t *             {@link #getQuantityText}\n\t *         </td>\n\t *     </tr>\n\t *\n\t *     </tbody>\n\t * </table>\n\t *\n\t * <p>Other resource types, such as\n\t * <a href=\"http://developer.android.com/guide/topics/resources/style-resource.html\">styles/themes</a>,\n\t * {@linkplain #openRawResource raw resources} and\n\t * <a href=\"http://developer.android.com/guide/topics/resources/more-resources.html#TypedArray\">typed arrays</a>\n\t * can't be replaced.\n\t *\n\t * <p><i>\n\t *    * Auto-boxing allows you to use literals like {@code 123} where an {@link Integer} is\n\t *      accepted, so you don't neeed to call methods like {@link Integer#valueOf(int)} manually.<br>\n\t *    ** Some of these methods have multiple variants, only one of them is mentioned here.\n\t * </i>\n\t *\n\t * @param pkg The package name, e.g. {@code com.example.myapplication}.\n\t *            See {@link #getResourcePackageName}.\n\t * @param type The type name, e.g. {@code string}.\n\t *            See {@link #getResourceTypeName}.\n\t * @param name The entry name, e.g. {@code app_name}.\n\t *            See {@link #getResourceEntryName}.\n\t * @param replacement The replacement.\n\t */\n\tpublic void setReplacement(String pkg, String type, String name, Object replacement) {\n\t\tint id = getIdentifier(name, type, pkg);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(pkg + \":\" + type + \"/\" + name);\n\t\tsetReplacement(id, replacement, this);\n\t}\n\n\t/**\n\t * Sets a replacement for an individual Android framework resource (in the {@code android} package).\n\t * See {@link #setSystemWideReplacement(String, String, String, Object)}.\n\t *\n\t * @param id The ID of the resource which should be replaced.\n\t * @param replacement The replacement.\n\t */\n\tpublic static void setSystemWideReplacement(int id, Object replacement) {\n\t\tsetReplacement(id, replacement, null);\n\t}\n\n\t/**\n\t * Sets a replacement for an individual Android framework resource (in the {@code android} package).\n\t * See {@link #setSystemWideReplacement(String, String, String, Object)}.\n\t *\n\t * @deprecated Use {@link #setSystemWideReplacement(String, String, String, Object)} instead.\n\t *\n\t * @param fullName The full resource name, e.g. {@code android:string/yes}.\n\t *                 See {@link #getResourceName}.\n\t * @param replacement The replacement.\n\t */\n\t@Deprecated\n\tpublic static void setSystemWideReplacement(String fullName, Object replacement) {\n\t\tint id = getSystem().getIdentifier(fullName, null, null);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(fullName);\n\t\tsetReplacement(id, replacement, null);\n\t}\n\n\t/**\n\t * Sets a replacement for an individual Android framework resource (in the {@code android} package).\n\t *\n\t * <p>Some resources are part of the Android framework and can be used in any app. They're\n\t * accessible via {@link android.R android.R} and are not bound to a specific\n\t * {@link android.content.res.Resources} instance. Such resources can be replaced in\n\t * {@link IXposedHookZygoteInit#initZygote initZygote()} for all apps. As there is no\n\t * {@link XResources} object easily available in that scope, this static method can be used\n\t * to set resource replacements. All other details (e.g. how certain types can be replaced) are\n\t * mentioned in {@link #setReplacement(String, String, String, Object)}.\n\t *\n\t * @param pkg The package name, should always be {@code android} here.\n\t *            See {@link #getResourcePackageName}.\n\t * @param type The type name, e.g. {@code string}.\n\t *            See {@link #getResourceTypeName}.\n\t * @param name The entry name, e.g. {@code yes}.\n\t *            See {@link #getResourceEntryName}.\n\t * @param replacement The replacement.\n\t */\n\tpublic static void setSystemWideReplacement(String pkg, String type, String name, Object replacement) {\n\t\tint id = getSystem().getIdentifier(name, type, pkg);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(pkg + \":\" + type + \"/\" + name);\n\t\tsetReplacement(id, replacement, null);\n\t}\n\n\tprivate static void setReplacement(int id, Object replacement, XResources res) {\n\t\tString resDir = (res != null) ? res.mResDir : null;\n\t\tif (res == null) {\n\t\t\ttry {\n\t\t\t\tXposedInit.hookResources();\n\t\t\t} catch (Throwable throwable) {\n\t\t\t\tthrow new IllegalStateException(\"Failed to initialize resources hook\");\n\t\t\t}\n\t\t}\n\t\tif (id == 0)\n\t\t\tthrow new IllegalArgumentException(\"id 0 is not an allowed resource identifier\");\n\t\telse if (resDir == null && id >= 0x7f000000)\n\t\t\tthrow new IllegalArgumentException(\"ids >= 0x7f000000 are app specific and cannot be set for the framework\");\n\n\t\tif (replacement instanceof Drawable)\n\t\t\tthrow new IllegalArgumentException(\"Drawable replacements are deprecated since Xposed 2.1. Use DrawableLoader instead.\");\n\n\t\t// Cache that we have a replacement for this ID, false positives are accepted to save memory.\n\t\tif (id < 0x7f000000) {\n\t\t\tint cacheKey = (id & 0x00070000) >> 11 | (id & 0xf8) >> 3;\n\t\t\tsynchronized (sSystemReplacementsCache) {\n\t\t\t\tsSystemReplacementsCache[cacheKey] |= 1 << (id & 7);\n\t\t\t}\n\t\t} else {\n\t\t\tint cacheKey = (id & 0x00070000) >> 12 | (id & 0x78) >> 3;\n\t\t\tsynchronized (res.mReplacementsCache) {\n\t\t\t\tres.mReplacementsCache[cacheKey] |= 1 << (id & 7);\n\t\t\t}\n\t\t}\n\n\t\tsynchronized (sReplacements) {\n\t\t\tHashMap<String, Object> inner = sReplacements.get(id);\n\t\t\tif (inner == null) {\n\t\t\t\tinner = new HashMap<>();\n\t\t\t\tsReplacements.put(id, inner);\n\t\t\t}\n\t\t\tinner.put(resDir, replacement);\n\t\t}\n\t}\n\n\t// =======================================================\n\t//   RETURNING REPLACEMENTS\n\t// =======================================================\n\n\tprivate Object getReplacement(int id) {\n\t\tif (id <= 0)\n\t\t\treturn null;\n\n\t\t// Check the cache whether it's worth looking for replacements\n\t\tif (id < 0x7f000000) {\n\t\t\tint cacheKey = (id & 0x00070000) >> 11 | (id & 0xf8) >> 3;\n\t\t\tif ((sSystemReplacementsCache[cacheKey] & (1 << (id & 7))) == 0)\n\t\t\t\treturn null;\n\t\t} else if (mResDir != null) {\n\t\t\tint cacheKey = (id & 0x00070000) >> 12 | (id & 0x78) >> 3;\n\t\t\tif ((mReplacementsCache[cacheKey] & (1 << (id & 7))) == 0)\n\t\t\t\treturn null;\n\t\t}\n\n\t\tHashMap<String, Object> inner;\n\t\tsynchronized (sReplacements) {\n\t\t\tinner = sReplacements.get(id);\n\t\t}\n\n\t\tif (inner == null)\n\t\t\treturn null;\n\n\t\tsynchronized (inner) {\n\t\t\tObject result = inner.get(mResDir);\n\t\t\tif (result != null || mResDir == null)\n\t\t\t\treturn result;\n\t\t\treturn inner.get(null);\n\t\t}\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic XmlResourceParser getAnimation(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\n\t\t\tboolean loadedFromCache = isXmlCached(repRes, repId);\n\t\t\tXmlResourceParser result = repRes.getAnimation(repId);\n\n\t\t\tif (!loadedFromCache) {\n\t\t\t\tlong parseState = getLongField(result, \"mParseState\");\n\t\t\t\trewriteXmlReferencesNative(parseState, this, repRes);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t\treturn super.getAnimation(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic boolean getBoolean(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof Boolean) {\n\t\t\treturn (Boolean) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getBoolean(repId);\n\t\t}\n\t\treturn super.getBoolean(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic int getColor(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof Integer) {\n\t\t\treturn (Integer) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getColor(repId);\n\t\t}\n\t\treturn super.getColor(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic ColorStateList getColorStateList(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof ColorStateList) {\n\t\t\treturn (ColorStateList) replacement;\n\t\t} else if (replacement instanceof Integer) {\n\t\t\tint color = (Integer) replacement;\n\t\t\tsynchronized (sColorStateListCache) {\n\t\t\t\tColorStateList result = sColorStateListCache.get(color);\n\t\t\t\tif (result == null) {\n\t\t\t\t\tresult = ColorStateList.valueOf(color);\n\t\t\t\t\tsColorStateListCache.put(color, result);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getColorStateList(repId);\n\t\t}\n\t\treturn super.getColorStateList(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic float getDimension(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof DimensionReplacement) {\n\t\t\treturn ((DimensionReplacement) replacement).getDimension(getDisplayMetrics());\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getDimension(repId);\n\t\t}\n\t\treturn super.getDimension(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic int getDimensionPixelOffset(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof DimensionReplacement) {\n\t\t\treturn ((DimensionReplacement) replacement).getDimensionPixelOffset(getDisplayMetrics());\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getDimensionPixelOffset(repId);\n\t\t}\n\t\treturn super.getDimensionPixelOffset(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic int getDimensionPixelSize(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof DimensionReplacement) {\n\t\t\treturn ((DimensionReplacement) replacement).getDimensionPixelSize(getDisplayMetrics());\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getDimensionPixelSize(repId);\n\t\t}\n\t\treturn super.getDimensionPixelSize(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic Drawable getDrawable(int id) throws NotFoundException {\n\t\ttry {\n\t\t\tif (incrementMethodDepth(\"getDrawable\") == 1) {\n\t\t\t\tObject replacement = getReplacement(id);\n\t\t\t\tif (replacement instanceof DrawableLoader) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tDrawable result = ((DrawableLoader) replacement).newDrawable(this, id);\n\t\t\t\t\t\tif (result != null)\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (Throwable t) { XposedBridge.log(t); }\n\t\t\t\t} else if (replacement instanceof Integer) {\n\t\t\t\t\treturn new ColorDrawable((Integer) replacement);\n\t\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\t\treturn repRes.getDrawable(repId);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn super.getDrawable(id);\n\t\t} finally {\n\t\t\tdecrementMethodDepth(\"getDrawable\");\n\t\t}\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic Drawable getDrawable(int id, Theme theme) throws NotFoundException {\n\t\ttry {\n\t\t\tif (incrementMethodDepth(\"getDrawable\") == 1) {\n\t\t\t\tObject replacement = getReplacement(id);\n\t\t\t\tif (replacement instanceof DrawableLoader) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tDrawable result = ((DrawableLoader) replacement).newDrawable(this, id);\n\t\t\t\t\t\tif (result != null)\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (Throwable t) { XposedBridge.log(t); }\n\t\t\t\t} else if (replacement instanceof Integer) {\n\t\t\t\t\treturn new ColorDrawable((Integer) replacement);\n\t\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\t\treturn repRes.getDrawable(repId);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn super.getDrawable(id, theme);\n\t\t} finally {\n\t\t\tdecrementMethodDepth(\"getDrawable\");\n\t\t}\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic Drawable getDrawableForDensity(int id, int density) throws NotFoundException {\n\t\ttry {\n\t\t\tif (incrementMethodDepth(\"getDrawableForDensity\") == 1) {\n\t\t\t\tObject replacement = getReplacement(id);\n\t\t\t\tif (replacement instanceof DrawableLoader) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tDrawable result = ((DrawableLoader) replacement).newDrawableForDensity(this, id, density);\n\t\t\t\t\t\tif (result != null)\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (Throwable t) { XposedBridge.log(t); }\n\t\t\t\t} else if (replacement instanceof Integer) {\n\t\t\t\t\treturn new ColorDrawable((Integer) replacement);\n\t\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\t\treturn repRes.getDrawableForDensity(repId, density);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn super.getDrawableForDensity(id, density);\n\t\t} finally {\n\t\t\tdecrementMethodDepth(\"getDrawableForDensity\");\n\t\t}\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic Drawable getDrawableForDensity(int id, int density, Theme theme) throws NotFoundException {\n\t\ttry {\n\t\t\tif (incrementMethodDepth(\"getDrawableForDensity\") == 1) {\n\t\t\t\tObject replacement = getReplacement(id);\n\t\t\t\tif (replacement instanceof DrawableLoader) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tDrawable result = ((DrawableLoader) replacement).newDrawableForDensity(this, id, density);\n\t\t\t\t\t\tif (result != null)\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (Throwable t) { XposedBridge.log(t); }\n\t\t\t\t} else if (replacement instanceof Integer) {\n\t\t\t\t\treturn new ColorDrawable((Integer) replacement);\n\t\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\t\treturn repRes.getDrawableForDensity(repId, density);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn super.getDrawableForDensity(id, density, theme);\n\t\t} finally {\n\t\t\tdecrementMethodDepth(\"getDrawableForDensity\");\n\t\t}\n\t}\n\n\t/** @hide */\n\t@RequiresApi(Build.VERSION_CODES.Q)\n\t@Override\n\tpublic float getFloat(int id) {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof Float) {\n\t\t\treturn (Float) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getFloat(repId);\n\t\t}\n\t\treturn super.getFloat(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic Typeface getFont(int id) {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof Typeface) {\n\t\t\treturn (Typeface) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getFont(repId);\n\t\t}\n\t\treturn super.getFont(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic float getFraction(int id, int base, int pbase) {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getFraction(repId, base, pbase);\n\t\t}\n\t\treturn super.getFraction(id, base, pbase);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic int getInteger(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof Integer) {\n\t\t\treturn (Integer) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getInteger(repId);\n\t\t}\n\t\treturn super.getInteger(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic int[] getIntArray(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof int[]) {\n\t\t\treturn (int[]) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getIntArray(repId);\n\t\t}\n\t\treturn super.getIntArray(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic XmlResourceParser getLayout(int id) throws NotFoundException {\n\t\tXmlResourceParser result;\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\n\t\t\tboolean loadedFromCache = isXmlCached(repRes, repId);\n\t\t\tresult = repRes.getLayout(repId);\n\n\t\t\tif (!loadedFromCache) {\n\t\t\t\tlong parseState = getLongField(result, \"mParseState\");\n\t\t\t\trewriteXmlReferencesNative(parseState, this, repRes);\n\t\t\t}\n\t\t} else {\n\t\t\tresult = super.getLayout(id);\n\t\t}\n\n\t\t// Check whether this layout is hooked\n\t\tHashMap<String, CopyOnWriteSortedSet<XC_LayoutInflated>> inner;\n\t\tsynchronized (sLayoutCallbacks) {\n\t\t\tinner = sLayoutCallbacks.get(id);\n\t\t}\n\t\tif (inner != null) {\n\t\t\tCopyOnWriteSortedSet<XC_LayoutInflated> callbacks;\n\t\t\tsynchronized (inner) {\n\t\t\t\tcallbacks = inner.get(mResDir);\n\t\t\t\tif (callbacks == null && mResDir != null)\n\t\t\t\t\tcallbacks = inner.get(null);\n\t\t\t}\n\t\t\tif (callbacks != null) {\n\t\t\t\tString variant = \"layout\";\n\t\t\t\tTypedValue value = (TypedValue) getObjectField(this, \"mTmpValue\");\n\t\t\t\tgetValue(id, value, true);\n\t\t\t\tif (value.type == TypedValue.TYPE_STRING) {\n\t\t\t\t\tString[] components = value.string.toString().split(\"/\", 3);\n\t\t\t\t\tif (components.length == 3)\n\t\t\t\t\t\tvariant = components[1];\n\t\t\t\t\telse\n\t\t\t\t\t\tXposedBridge.log(\"Unexpected resource path \\\"\" + value.string.toString()\n\t\t\t\t\t\t\t\t+ \"\\\" for resource id 0x\" + Integer.toHexString(id));\n\t\t\t\t} else {\n\t\t\t\t\tXposedBridge.log(new NotFoundException(\"Could not find file name for resource id 0x\") + Integer.toHexString(id));\n\t\t\t\t}\n\n\t\t\t\tsynchronized (sXmlInstanceDetails) {\n\t\t\t\t\tsynchronized (sResourceNames) {\n\t\t\t\t\t\tHashMap<String, ResourceNames> resNamesInner = sResourceNames.get(id);\n\t\t\t\t\t\tif (resNamesInner != null) {\n\t\t\t\t\t\t\tsynchronized (resNamesInner) {\n\t\t\t\t\t\t\t\tXMLInstanceDetails details = new XMLInstanceDetails(resNamesInner.get(mResDir), variant, callbacks);\n\t\t\t\t\t\t\t\tsXmlInstanceDetails.put(result, details);\n\n\t\t\t\t\t\t\t\t// if we were called inside LayoutInflater.parseInclude, store the details for it\n\t\t\t\t\t\t\t\tMethodHookParam top = sIncludedLayouts.get().peek();\n\t\t\t\t\t\t\t\tif (top != null)\n\t\t\t\t\t\t\t\t\ttop.setObjectExtra(EXTRA_XML_INSTANCE_DETAILS, details);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic Movie getMovie(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getMovie(repId);\n\t\t}\n\t\treturn super.getMovie(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic CharSequence getQuantityText(int id, int quantity) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getQuantityText(repId, quantity);\n\t\t}\n\t\treturn super.getQuantityText(id, quantity);\n\t}\n\t// these are handled by getQuantityText:\n\t// public String getQuantityString(int id, int quantity);\n\t// public String getQuantityString(int id, int quantity, Object... formatArgs);\n\n\t/** @hide */\n\t@Override\n\tpublic String[] getStringArray(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof String[]) {\n\t\t\treturn (String[]) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getStringArray(repId);\n\t\t}\n\t\treturn super.getStringArray(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic CharSequence getText(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof CharSequence) {\n\t\t\treturn (CharSequence) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getText(repId);\n\t\t}\n\t\treturn super.getText(id);\n\t}\n\t// these are handled by getText:\n\t// public String getString(int id);\n\t// public String getString(int id, Object... formatArgs);\n\n\t/** @hide */\n\t@Override\n\tpublic CharSequence getText(int id, CharSequence def) {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof CharSequence) {\n\t\t\treturn (CharSequence) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getText(repId, def);\n\t\t}\n\t\treturn super.getText(id, def);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic CharSequence[] getTextArray(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof CharSequence[]) {\n\t\t\treturn (CharSequence[]) replacement;\n\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\treturn repRes.getTextArray(repId);\n\t\t}\n\t\treturn super.getTextArray(id);\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic void getValue(int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\trepRes.getValue(repId, outValue, resolveRefs);\n\t\t} else {\n\t\t\tif (replacement != null) {\n\t\t\t\tXposedBridge.log(\"Replacement of resource ID #0x\" + Integer.toHexString(id) + \" escaped because of deprecated replacement. Please use XResForwarder instead.\");\n\t\t\t}\n\t\t\tsuper.getValue(id, outValue, resolveRefs);\n\t\t}\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\trepRes.getValueForDensity(repId, density, outValue, resolveRefs);\n\t\t} else {\n\t\t\tif (replacement != null) {\n\t\t\t\tXposedBridge.log(\"Replacement of resource ID #0x\" + Integer.toHexString(id) + \" escaped because of deprecated replacement. Please use XResForwarder instead.\");\n\t\t\t}\n\t\t\tsuper.getValueForDensity(id, density, outValue, resolveRefs);\n\t\t}\n\t}\n\n\t/** @hide */\n\t@Override\n\tpublic XmlResourceParser getXml(int id) throws NotFoundException {\n\t\tObject replacement = getReplacement(id);\n\t\tif (replacement instanceof XResForwarder) {\n\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\tint repId = ((XResForwarder) replacement).getId();\n\n\t\t\tboolean loadedFromCache = isXmlCached(repRes, repId);\n\t\t\tXmlResourceParser result = repRes.getXml(repId);\n\n\t\t\tif (!loadedFromCache) {\n\t\t\t\tlong parseState = getLongField(result, \"mParseState\");\n\t\t\t\trewriteXmlReferencesNative(parseState, this, repRes);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t\treturn super.getXml(id);\n\t}\n\n\tprivate static boolean isXmlCached(Resources res, int id) {\n\t\tint[] mCachedXmlBlockIds = (int[]) getObjectField(getObjectField(res, \"mResourcesImpl\"), \"mCachedXmlBlockCookies\");\n\t\tsynchronized (mCachedXmlBlockIds) {\n\t\t\tfor (int cachedId : mCachedXmlBlockIds) {\n\t\t\t\tif (cachedId == id)\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Used to replace reference IDs in XMLs.\n\t *\n\t * When resource requests are forwarded to modules, the may include references to resources with the same\n\t * name as in the original resources, but the IDs generated by aapt will be different. rewriteXmlReferencesNative\n\t * walks through all references and calls this function to find out the original ID, which it then writes to\n\t * the compiled XML file in the memory.\n\t */\n\tprivate static int translateResId(int id, XResources origRes, Resources repRes) {\n\t\ttry {\n\t\t\tString entryName = repRes.getResourceEntryName(id);\n\t\t\tString entryType = repRes.getResourceTypeName(id);\n\t\t\tString origPackage = origRes.mPackageName;\n\t\t\tint origResId = 0;\n\t\t\ttry {\n\t\t\t\t// look for a resource with the same name and type in the original package\n\t\t\t\torigResId = origRes.getIdentifier(entryName, entryType, origPackage);\n\t\t\t} catch (NotFoundException ignored) {}\n\n\t\t\tboolean repResDefined = false;\n\t\t\ttry {\n\t\t\t\tfinal TypedValue tmpValue = new TypedValue();\n\t\t\t\trepRes.getValue(id, tmpValue, false);\n\t\t\t\t// if a resource has not been defined (i.e. only a resource ID has been created), it will equal \"false\"\n\t\t\t\t// this means a boolean \"false\" value is not detected of it is directly referenced in an XML file\n\t\t\t\trepResDefined = !(tmpValue.type == TypedValue.TYPE_INT_BOOLEAN && tmpValue.data == 0);\n\t\t\t} catch (NotFoundException ignored) {}\n\n\t\t\tif (!repResDefined && origResId == 0 && !entryType.equals(\"id\")) {\n\t\t\t\tXposedBridge.log(entryType + \"/\" + entryName + \" is neither defined in module nor in original resources\");\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t// exists only in module, so create a fake resource id\n\t\t\tif (origResId == 0)\n\t\t\t\torigResId = getFakeResId(repRes, id);\n\n\t\t\t// IDs will never be loaded, no need to set a replacement\n\t\t\tif (repResDefined && !entryType.equals(\"id\"))\n\t\t\t\torigRes.setReplacement(origResId, new XResForwarder(repRes, id));\n\n\t\t\treturn origResId;\n\t\t} catch (Exception e) {\n\t\t\tXposedBridge.log(e);\n\t\t\treturn id;\n\t\t}\n\t}\n\n\t/**\n\t * Generates a fake resource ID.\n\t *\n\t * <p>The parameter is just hashed, it doesn't have a deeper meaning. However, it's recommended\n\t * to use values with a low risk for conflicts, such as a full resource name. Calling this\n\t * method multiple times will return the same ID.\n\t *\n\t * @param resName A used for hashing, see above.\n\t * @return The fake resource ID.\n\t */\n\tpublic static int getFakeResId(String resName) {\n\t\treturn 0x7e000000 | (resName.hashCode() & 0x00ffffff);\n\t}\n\n\t/**\n\t * Generates a fake resource ID.\n\t *\n\t * <p>This variant uses the result of {@link #getResourceName} to create the hash that the ID is\n\t * based on. The given resource doesn't need to match the {@link XResources} instance for which\n\t * the fake resource ID is going to be used.\n\t *\n\t * @param res The {@link android.content.res.Resources} object to be used for hashing.\n\t * @param id The resource ID to be used for hashing.\n\t * @return The fake resource ID.\n\t */\n\tpublic static int getFakeResId(Resources res, int id) {\n\t\treturn getFakeResId(res.getResourceName(id));\n\t}\n\n\t/**\n\t * Makes any individual resource available from another {@link android.content.res.Resources}\n\t * instance available in this {@link XResources} instance.\n\t *\n\t * <p>This method combines calls to {@link #getFakeResId(Resources, int)} and\n\t * {@link #setReplacement(int, Object)} to generate a fake resource ID and set up a replacement\n\t * for it which forwards to the given resource.\n\t *\n\t * <p>The returned ID can only be used to retrieve the resource, it won't work for methods like\n\t * {@link #getResourceName} etc.\n\t *\n\t * @param res The target {@link android.content.res.Resources} instance.\n\t * @param id The target resource ID.\n\t * @return The fake resource ID (see above).\n\t */\n\tpublic int addResource(Resources res, int id) {\n\t\tint fakeId = getFakeResId(res, id);\n\t\tsynchronized (sReplacements) {\n\t\t\tif (sReplacements.indexOfKey(fakeId) < 0)\n\t\t\t\tsetReplacement(fakeId, new XResForwarder(res, id));\n\t\t}\n\t\treturn fakeId;\n\t}\n\n\t/**\n\t * Similar to {@link #translateResId}, but used to determine the original ID of attribute names.\n\t */\n\tprivate static int translateAttrId(String attrName, XResources origRes) {\n\t\tString origPackage = origRes.mPackageName;\n\t\tint origAttrId = 0;\n\t\ttry {\n\t\t\torigAttrId = origRes.getIdentifier(attrName, \"attr\", origPackage);\n\t\t} catch (NotFoundException e) {\n\t\t\tXposedBridge.log(\"Attribute \" + attrName + \" not found in original resources\");\n\t\t}\n\t\treturn origAttrId;\n\t}\n\n\t// =======================================================\n\t//   XTypedArray class\n\t// =======================================================\n\t/**\n\t * {@link android.content.res.TypedArray} replacement that replaces values on-the-fly.\n\t * Mainly used when inflating layouts.\n\t * @hide\n\t */\n\tpublic static class XTypedArray extends XTypedArraySuperClass {\n\n        public XTypedArray(Resources resources) {\n            super(resources);\n        }\n\n        /** Dummy, will never be called (objects are transferred to this class only). */\n//\t\tprivate XTypedArray() {\n//\t\t\tsuper(null, null, null, 0);\n//\t\t\tthrow new UnsupportedOperationException();\n//\t\t}\n\n\t\t@Override\n\t\tpublic boolean getBoolean(int index, boolean defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof Boolean) {\n\t\t\t\treturn (Boolean) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getBoolean(repId);\n\t\t\t}\n\t\t\treturn super.getBoolean(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getColor(int index, int defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof Integer) {\n\t\t\t\treturn (Integer) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getColor(repId);\n\t\t\t}\n\t\t\treturn super.getColor(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic ColorStateList getColorStateList(int index) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof ColorStateList) {\n\t\t\t\treturn (ColorStateList) replacement;\n\t\t\t} else if (replacement instanceof Integer) {\n\t\t\t\tint color = (Integer) replacement;\n\t\t\t\tsynchronized (sColorStateListCache) {\n\t\t\t\t\tColorStateList result = sColorStateListCache.get(color);\n\t\t\t\t\tif (result == null) {\n\t\t\t\t\t\tresult = ColorStateList.valueOf(color);\n\t\t\t\t\t\tsColorStateListCache.put(color, result);\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getColorStateList(repId);\n\t\t\t}\n\t\t\treturn super.getColorStateList(index);\n\t\t}\n\n\t\t@Override\n\t\tpublic float getDimension(int index, float defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getDimension(repId);\n\t\t\t}\n\t\t\treturn super.getDimension(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getDimensionPixelOffset(int index, int defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getDimensionPixelOffset(repId);\n\t\t\t}\n\t\t\treturn super.getDimensionPixelOffset(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getDimensionPixelSize(int index, int defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getDimensionPixelSize(repId);\n\t\t\t}\n\t\t\treturn super.getDimensionPixelSize(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic Drawable getDrawable(int index) {\n\t\t\tfinal int resId = getResourceId(index, 0);\n\t\t\tXResources xres = (XResources) getResources();\n\t\t\tObject replacement = xres.getReplacement(resId);\n\t\t\tif (replacement instanceof DrawableLoader) {\n\t\t\t\ttry {\n\t\t\t\t\tDrawable result = ((DrawableLoader) replacement).newDrawable(xres, resId);\n\t\t\t\t\tif (result != null)\n\t\t\t\t\t\treturn result;\n\t\t\t\t} catch (Throwable t) { XposedBridge.log(t); }\n\t\t\t} else if (replacement instanceof Integer) {\n\t\t\t\treturn new ColorDrawable((Integer) replacement);\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getDrawable(repId);\n\t\t\t}\n\t\t\treturn super.getDrawable(index);\n\t\t}\n\n\t\t@Override\n\t\tpublic float getFloat(int index, float defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\t// dimensions seem to be the only way to define floats by references\n\t\t\t\treturn repRes.getDimension(repId);\n\t\t\t}\n\t\t\treturn super.getFloat(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic Typeface getFont(int index) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof Typeface) {\n\t\t\t\treturn (Typeface) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getFont(repId);\n\t\t\t}\n\t\t\treturn super.getFont(index);\n\t\t}\n\n\t\t@Override\n\t\tpublic float getFraction(int index, int base, int pbase, float defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\t// dimensions seem to be the only way to define floats by references\n\t\t\t\treturn repRes.getFraction(repId, base, pbase);\n\t\t\t}\n\t\t\treturn super.getFraction(index, base, pbase, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getInt(int index, int defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof Integer) {\n\t\t\t\treturn (Integer) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getInteger(repId);\n\t\t\t}\n\t\t\treturn super.getInt(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getInteger(int index, int defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof Integer) {\n\t\t\t\treturn (Integer) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getInteger(repId);\n\t\t\t}\n\t\t\treturn super.getInteger(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getLayoutDimension(int index, int defValue) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getDimensionPixelSize(repId);\n\t\t\t}\n\t\t\treturn super.getLayoutDimension(index, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getLayoutDimension(int index, String name) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getDimensionPixelSize(repId);\n\t\t\t}\n\t\t\treturn super.getLayoutDimension(index, name);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getString(int index) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof CharSequence) {\n\t\t\t\treturn replacement.toString();\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getString(repId);\n\t\t\t}\n\t\t\treturn super.getString(index);\n\t\t}\n\n\t\t@Override\n\t\tpublic CharSequence getText(int index) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof CharSequence) {\n\t\t\t\treturn (CharSequence) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getText(repId);\n\t\t\t}\n\t\t\treturn super.getText(index);\n\t\t}\n\n\t\t@Override\n\t\tpublic CharSequence[] getTextArray(int index) {\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(getResourceId(index, 0));\n\t\t\tif (replacement instanceof CharSequence[]) {\n\t\t\t\treturn (CharSequence[]) replacement;\n\t\t\t} else if (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\treturn repRes.getTextArray(repId);\n\t\t\t}\n\t\t\treturn super.getTextArray(index);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean getValue(int index, TypedValue outValue) {\n\t\t\tvar id = getResourceId(index, 0);\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(id);\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\trepRes.getValue(repId, outValue, true);\n\t\t\t\treturn outValue.type != TypedValue.TYPE_NULL;\n\t\t\t} else {\n\t\t\t\tif (replacement != null) {\n\t\t\t\t\tXposedBridge.log(\"Replacement of resource ID #0x\" + Integer.toHexString(id) + \" escaped because of deprecated replacement. Please use XResForwarder instead.\");\n\t\t\t\t}\n\t\t\t\treturn super.getValue(index, outValue);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic TypedValue peekValue(int index) {\n\t\t\tvar id = getResourceId(index, 0);\n\t\t\tObject replacement = ((XResources) getResources()).getReplacement(id);\n\t\t\tif (replacement instanceof XResForwarder) {\n\t\t\t\tif (getBooleanField(this, \"mRecycled\")) {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot make calls to a recycled instance!\");\n\t\t\t\t}\n\t\t\t\tfinal TypedValue value = (TypedValue) getObjectField(this, \"mValue\");\n\t\t\t\tResources repRes = ((XResForwarder) replacement).getResources();\n\t\t\t\tint repId = ((XResForwarder) replacement).getId();\n\t\t\t\trepRes.getValue(repId, value, true);\n\t\t\t\treturn value;\n\t\t\t} else {\n\t\t\t\tif (replacement != null) {\n\t\t\t\t\tXposedBridge.log(\"Replacement of resource ID #0x\" + Integer.toHexString(id) + \" escaped because of deprecated replacement. Please use XResForwarder instead.\");\n\t\t\t\t}\n\t\t\t\treturn super.peekValue(index);\n\t\t\t}\n\t\t}\n\t}\n\n\n\t// =======================================================\n\t//   DrawableLoader class\n\t// =======================================================\n\t/**\n\t * Callback for drawable replacements. Instances of this class can passed to\n\t * {@link #setReplacement(String, String, String, Object)} and its variants.\n\t *\n\t * <p class=\"caution\">Make sure to always return new {@link Drawable} instances, as drawables\n\t * usually can't be reused.\n\t */\n\t@SuppressWarnings(\"UnusedParameters\")\n\tpublic static abstract class DrawableLoader {\n\t\t/**\n\t\t * Constructor.\n\t\t */\n\t\tpublic DrawableLoader() {}\n\n\t\t/**\n\t\t * Called when the hooked drawable resource has been requested.\n\t\t *\n\t\t * @param res The {@link XResources} object in which the hooked drawable resides.\n\t\t * @param id The resource ID which has been requested.\n\t\t * @return The {@link Drawable} which should be used as replacement. {@code null} is ignored.\n\t\t * @throws Throwable Everything the callback throws is caught and logged.\n\t\t */\n\t\tpublic abstract Drawable newDrawable(XResources res, int id) throws Throwable;\n\n\t\t/**\n\t\t * Like {@link #newDrawable}, but called for {@link #getDrawableForDensity}. The default\n\t\t * implementation is to use the result of {@link #newDrawable}.\n\t\t *\n\t\t * @param res The {@link XResources} object in which the hooked drawable resides.\n\t\t * @param id The resource ID which has been requested.\n\t\t * @param density The desired screen density indicated by the resource as found in\n\t\t *                {@link DisplayMetrics}.\n\t\t * @return The {@link Drawable} which should be used as replacement. {@code null} is ignored.\n\t\t * @throws Throwable Everything the callback throws is caught and logged.\n\t\t */\n\t\tpublic Drawable newDrawableForDensity(XResources res, int id, int density) throws Throwable {\n\t\t\treturn newDrawable(res, id);\n\t\t}\n\t}\n\n\n\t// =======================================================\n\t//   DimensionReplacement class\n\t// =======================================================\n\t/**\n\t * Callback for dimension replacements. Instances of this class can passed to\n\t * {@link #setReplacement(String, String, String, Object)} and its variants.\n\t */\n\tpublic static class DimensionReplacement {\n\t\tprivate final float mValue;\n\t\tprivate final int mUnit;\n\n\t\t/**\n\t\t * Creates an instance that can be used for {@link #setReplacement(String, String, String, Object)}\n\t\t * to replace a dimension resource.\n\t\t *\n\t\t * @param value The value of the replacement, in the unit specified with the next parameter.\n\t\t * @param unit One of the {@code COMPLEX_UNIT_*} constants in {@link TypedValue}.\n\t\t */\n\t\tpublic DimensionReplacement(float value, int unit) {\n\t\t\tmValue = value;\n\t\t\tmUnit = unit;\n\t\t}\n\n\t\t/** Called by {@link android.content.res.Resources#getDimension}. */\n\t\tpublic float getDimension(DisplayMetrics metrics) {\n\t\t\treturn TypedValue.applyDimension(mUnit, mValue, metrics);\n\t\t}\n\n\t\t/** Called by {@link android.content.res.Resources#getDimensionPixelOffset}. */\n\t\tpublic int getDimensionPixelOffset(DisplayMetrics metrics) {\n\t\t\treturn (int) TypedValue.applyDimension(mUnit, mValue, metrics);\n\t\t}\n\n\t\t/** Called by {@link android.content.res.Resources#getDimensionPixelSize}. */\n\t\tpublic int getDimensionPixelSize(DisplayMetrics metrics) {\n\t\t\tfinal float f = TypedValue.applyDimension(mUnit, mValue, metrics);\n\t\t\tfinal int res = (int)(f+0.5f);\n\t\t\tif (res != 0) return res;\n\t\t\tif (mValue == 0) return 0;\n\t\t\tif (mValue > 0) return 1;\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t// =======================================================\n\t//   INFLATING LAYOUTS\n\t// =======================================================\n\n\tprivate class XMLInstanceDetails {\n\t\tpublic final ResourceNames resNames;\n\t\tpublic final String variant;\n\t\tpublic final CopyOnWriteSortedSet<XC_LayoutInflated> callbacks;\n\t\tpublic final XResources res = XResources.this;\n\n\t\tprivate XMLInstanceDetails(ResourceNames resNames, String variant, CopyOnWriteSortedSet<XC_LayoutInflated> callbacks) {\n\t\t\tthis.resNames = resNames;\n\t\t\tthis.variant = variant;\n\t\t\tthis.callbacks = callbacks;\n\t\t}\n\t}\n\n\t/**\n\t * Hook the inflation of a layout.\n\t *\n\t * @param id The ID of the resource which should be replaced.\n\t * @param callback The callback to be executed when the layout has been inflated.\n\t * @return An object which can be used to remove the callback again.\n\t */\n\tpublic XC_LayoutInflated.Unhook hookLayout(int id, XC_LayoutInflated callback) {\n\t\treturn hookLayoutInternal(mResDir, id, getResourceNames(id), callback);\n\t}\n\n\t/**\n\t * Hook the inflation of a layout.\n\t *\n\t * @deprecated Use {@link #hookLayout(String, String, String, XC_LayoutInflated)} instead.\n\t *\n\t * @param fullName The full resource name, e.g. {@code com.android.systemui:layout/statusbar}.\n\t *                 See {@link #getResourceName}.\n\t * @param callback The callback to be executed when the layout has been inflated.\n\t * @return An object which can be used to remove the callback again.\n\t */\n\t@Deprecated\n\tpublic XC_LayoutInflated.Unhook hookLayout(String fullName, XC_LayoutInflated callback) {\n\t\tint id = getIdentifier(fullName, null, null);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(fullName);\n\t\treturn hookLayout(id, callback);\n\t}\n\n\t/**\n\t * Hook the inflation of a layout.\n\t *\n\t * @param pkg The package name, e.g. {@code com.android.systemui}.\n\t *            See {@link #getResourcePackageName}.\n\t * @param type The type name, e.g. {@code layout}.\n\t *            See {@link #getResourceTypeName}.\n\t * @param name The entry name, e.g. {@code statusbar}.\n\t *            See {@link #getResourceEntryName}.\n\t * @param callback The callback to be executed when the layout has been inflated.\n\t * @return An object which can be used to remove the callback again.\n\t */\n\tpublic XC_LayoutInflated.Unhook hookLayout(String pkg, String type, String name, XC_LayoutInflated callback) {\n\t\tint id = getIdentifier(name, type, pkg);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(pkg + \":\" + type + \"/\" + name);\n\t\treturn hookLayout(id, callback);\n\t}\n\n\t/**\n\t * Hook the inflation of an Android framework layout (in the {@code android} package).\n\t * See {@link #hookSystemWideLayout(String, String, String, XC_LayoutInflated)}.\n\t *\n\t * @param id The ID of the resource which should be replaced.\n\t * @param callback The callback to be executed when the layout has been inflated.\n\t * @return An object which can be used to remove the callback again.\n\t */\n\tpublic static XC_LayoutInflated.Unhook hookSystemWideLayout(int id, XC_LayoutInflated callback) {\n\t\tif (id >= 0x7f000000)\n\t\t\tthrow new IllegalArgumentException(\"ids >= 0x7f000000 are app specific and cannot be set for the framework\");\n\t\treturn hookLayoutInternal(null, id, getSystemResourceNames(id), callback);\n\t}\n\n\t/**\n\t * Hook the inflation of an Android framework layout (in the {@code android} package).\n\t * See {@link #hookSystemWideLayout(String, String, String, XC_LayoutInflated)}.\n\t *\n\t * @deprecated Use {@link #hookSystemWideLayout(String, String, String, XC_LayoutInflated)} instead.\n\t *\n\t * @param fullName The full resource name, e.g. {@code android:layout/simple_list_item_1}.\n\t *                 See {@link #getResourceName}.\n\t * @param callback The callback to be executed when the layout has been inflated.\n\t * @return An object which can be used to remove the callback again.\n\t */\n\t@Deprecated\n\tpublic static XC_LayoutInflated.Unhook hookSystemWideLayout(String fullName, XC_LayoutInflated callback) {\n\t\tint id = getSystem().getIdentifier(fullName, null, null);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(fullName);\n\t\treturn hookSystemWideLayout(id, callback);\n\t}\n\n\t/**\n\t * Hook the inflation of an Android framework layout (in the {@code android} package).\n\t *\n\t * <p>Some layouts are part of the Android framework and can be used in any app. They're\n\t * accessible via {@link android.R.layout android.R.layout} and are not bound to a specific\n\t * {@link android.content.res.Resources} instance. Such resources can be replaced in\n\t * {@link IXposedHookZygoteInit#initZygote initZygote()} for all apps. As there is no\n\t * {@link XResources} object easily available in that scope, this static method can be used\n\t * to hook layouts.\n\t *\n\t * @param pkg The package name, e.g. {@code android}.\n\t *            See {@link #getResourcePackageName}.\n\t * @param type The type name, e.g. {@code layout}.\n\t *            See {@link #getResourceTypeName}.\n\t * @param name The entry name, e.g. {@code simple_list_item_1}.\n\t *            See {@link #getResourceEntryName}.\n\t * @param callback The callback to be executed when the layout has been inflated.\n\t * @return An object which can be used to remove the callback again.\n\t */\n\tpublic static XC_LayoutInflated.Unhook hookSystemWideLayout(String pkg, String type, String name, XC_LayoutInflated callback) {\n\t\tint id = getSystem().getIdentifier(name, type, pkg);\n\t\tif (id == 0)\n\t\t\tthrow new NotFoundException(pkg + \":\" + type + \"/\" + name);\n\t\treturn hookSystemWideLayout(id, callback);\n\t}\n\n\tprivate static XC_LayoutInflated.Unhook hookLayoutInternal(String resDir, int id, ResourceNames resNames, XC_LayoutInflated callback) {\n\t\tif (id == 0)\n\t\t\tthrow new IllegalArgumentException(\"id 0 is not an allowed resource identifier\");\n\n\t\tif (resDir == null) {\n\t\t\ttry {\n\t\t\t\tXposedInit.hookResources();\n\t\t\t} catch (Throwable throwable) {\n\t\t\t\tthrow new IllegalStateException(\"Failed to initialize resources hook\", throwable);\n\t\t\t}\n\t\t}\n\n\t\tHashMap<String, CopyOnWriteSortedSet<XC_LayoutInflated>> inner;\n\t\tsynchronized (sLayoutCallbacks) {\n\t\t\tinner = sLayoutCallbacks.get(id);\n\t\t\tif (inner == null) {\n\t\t\t\tinner = new HashMap<>();\n\t\t\t\tsLayoutCallbacks.put(id, inner);\n\t\t\t}\n\t\t}\n\n\t\tCopyOnWriteSortedSet<XC_LayoutInflated> callbacks;\n\t\tsynchronized (inner) {\n\t\t\tcallbacks = inner.get(resDir);\n\t\t\tif (callbacks == null) {\n\t\t\t\tcallbacks = new CopyOnWriteSortedSet<>();\n\t\t\t\tinner.put(resDir, callbacks);\n\t\t\t}\n\t\t}\n\n\t\tcallbacks.add(callback);\n\n\t\tputResourceNames(resDir, resNames);\n\n\t\treturn callback.new Unhook(resDir, id);\n\t}\n\n\t/** @hide */\n\tpublic static void unhookLayout(String resDir, int id, XC_LayoutInflated callback) {\n\t\tHashMap<String, CopyOnWriteSortedSet<XC_LayoutInflated>> inner;\n\t\tsynchronized (sLayoutCallbacks) {\n\t\t\tinner = sLayoutCallbacks.get(id);\n\t\t\tif (inner == null)\n\t\t\t\treturn;\n\t\t}\n\n\t\tCopyOnWriteSortedSet<XC_LayoutInflated> callbacks;\n\t\tsynchronized (inner) {\n\t\t\tcallbacks = inner.get(resDir);\n\t\t\tif (callbacks == null)\n\t\t\t\treturn;\n\t\t}\n\n\t\tcallbacks.remove(callback);\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/IXposedHookCmdInit.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\n\n/**\n * Hook the initialization of Java-based command-line tools (like pm).\n *\n * @hide Xposed no longer hooks command-line tools, therefore this interface shouldn't be\n * implemented anymore.\n */\npublic interface IXposedHookCmdInit extends IXposedMod {\n    /**\n     * Called very early during startup of a command-line tool.\n     *\n     * @param startupParam Details about the module itself and the started process.\n     * @throws Throwable Everything is caught, but it will prevent further initialization of the module.\n     */\n    void initCmdApp(StartupParam startupParam) throws Throwable;\n\n    /**\n     * Data holder for {@link #initCmdApp}.\n     */\n    final class StartupParam {\n        /*package*/ StartupParam() {\n        }\n\n        /**\n         * The path to the module's APK.\n         */\n        public String modulePath;\n\n        /**\n         * The class name of the tools that the hook was invoked for.\n         */\n        public String startClassName;\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/IXposedHookInitPackageResources.java",
    "content": "package de.robv.android.xposed;\n\nimport android.content.res.XResources;\n\nimport de.robv.android.xposed.callbacks.XC_InitPackageResources;\nimport de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;\n\n/**\n * Get notified when the resources for an app are initialized.\n * In {@link #handleInitPackageResources}, resource replacements can be created.\n *\n * <p>This interface should be implemented by the module's main class. Xposed will take care of\n * registering it as a callback automatically.\n */\npublic interface IXposedHookInitPackageResources extends IXposedMod {\n    /**\n     * This method is called when resources for an app are being initialized.\n     * Modules can call special methods of the {@link XResources} class in order to replace resources.\n     *\n     * @param resparam Information about the resources.\n     * @throws Throwable Everything the callback throws is caught and logged.\n     */\n    void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable;\n\n    /** @hide */\n    final class Wrapper extends XC_InitPackageResources {\n        private final IXposedHookInitPackageResources instance;\n        public Wrapper(IXposedHookInitPackageResources instance) {\n            this.instance = instance;\n        }\n        @Override\n        public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {\n            instance.handleInitPackageResources(resparam);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/IXposedHookLoadPackage.java",
    "content": "package de.robv.android.xposed;\n\nimport android.app.Application;\n\nimport de.robv.android.xposed.callbacks.XC_LoadPackage;\nimport de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;\n\n/**\n * Get notified when an app (\"Android package\") is loaded.\n * This is especially useful to hook some app-specific methods.\n *\n * <p>This interface should be implemented by the module's main class. Xposed will take care of\n * registering it as a callback automatically.\n */\npublic interface IXposedHookLoadPackage extends IXposedMod {\n    /**\n     * This method is called when an app is loaded. It's called very early, even before\n     * {@link Application#onCreate} is called.\n     * Modules can set up their app-specific hooks here.\n     *\n     * @param lpparam Information about the app.\n     * @throws Throwable Everything the callback throws is caught and logged.\n     */\n    void handleLoadPackage(LoadPackageParam lpparam) throws Throwable;\n\n    /** @hide */\n    final class Wrapper extends XC_LoadPackage {\n        private final IXposedHookLoadPackage instance;\n        public Wrapper(IXposedHookLoadPackage instance) {\n            this.instance = instance;\n        }\n        @Override\n        public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {\n            instance.handleLoadPackage(lpparam);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/IXposedHookZygoteInit.java",
    "content": "package de.robv.android.xposed;\n\n/**\n * Hook the initialization of Zygote process(es), from which all the apps are forked.\n *\n * <p>Implement this interface in your module's main class in order to be notified when Android is\n * starting up. In {@link IXposedHookZygoteInit}, you can modify objects and place hooks that should\n * be applied for every app. Only the Android framework/system classes are available at that point\n * in time. Use {@code null} as class loader for {@link XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)}\n * and its variants.\n *\n * <p>If you want to hook one/multiple specific apps, use {@link IXposedHookLoadPackage} instead.\n */\npublic interface IXposedHookZygoteInit extends IXposedMod {\n    /**\n     * Called very early during startup of Zygote.\n     * @param startupParam Details about the module itself and the started process.\n     * @throws Throwable everything is caught, but will prevent further initialization of the module.\n     */\n    void initZygote(StartupParam startupParam) throws Throwable;\n\n    /** Data holder for {@link #initZygote}. */\n    final class StartupParam {\n        /*package*/ StartupParam() {}\n\n        /** The path to the module's APK. */\n        public String modulePath;\n\n        /**\n         * Always {@code true} on 32-bit ROMs. On 64-bit, it's only {@code true} for the primary\n         * process that starts the system_server.\n         */\n        public boolean startsSystemServer;\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/IXposedMod.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\n/**\n * Marker interface for Xposed modules. Cannot be implemented directly.\n */\n/* package */ interface IXposedMod {\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/SELinuxHelper.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport de.robv.android.xposed.services.BaseService;\nimport de.robv.android.xposed.services.DirectAccessService;\n\n/**\n * A helper to work with (or without) SELinux, abstracting much of its big complexity.\n */\npublic final class SELinuxHelper {\n    private SELinuxHelper() {\n    }\n\n    /**\n     * Determines whether SELinux is disabled or enabled.\n     *\n     * @return A boolean indicating whether SELinux is enabled.\n     */\n    public static boolean isSELinuxEnabled() {\n        // lsp: always enabled\n        return true;\n    }\n\n    /**\n     * Determines whether SELinux is permissive or enforcing.\n     *\n     * @return A boolean indicating whether SELinux is enforcing.\n     */\n    public static boolean isSELinuxEnforced() {\n        // lsp: always enforcing\n        return true;\n    }\n\n    /**\n     * Gets the security context of the current process.\n     *\n     * @return A String representing the security context of the current process.\n     */\n    public static String getContext() {\n        return null;\n    }\n\n    /**\n     * Retrieve the service to be used when accessing files in {@code /data/data/*}.\n     *\n     * <p class=\"caution\"><strong>IMPORTANT:</strong> If you call this from the Zygote process,\n     * don't re-use the result in different process!\n     *\n     * @return An instance of the service.\n     */\n    public static BaseService getAppDataFileService() {\n        return sServiceAppDataFile;\n    }\n\n    private static final BaseService sServiceAppDataFile = new DirectAccessService(); // ed: initialized directly\n\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/XC_MethodHook.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Member;\nimport java.util.HashMap;\n\nimport de.robv.android.xposed.callbacks.IXUnhook;\nimport de.robv.android.xposed.callbacks.XCallback;\n\n/**\n * Callback class for method hooks.\n *\n * <p>Usually, anonymous subclasses of this class are created which override\n * {@link #beforeHookedMethod} and/or {@link #afterHookedMethod}.\n */\npublic abstract class XC_MethodHook extends XCallback {\n    /**\n     * Creates a new callback with default priority.\n     */\n    @SuppressWarnings(\"deprecation\")\n    public XC_MethodHook() {\n        super();\n    }\n\n    /**\n     * Creates a new callback with a specific priority.\n     *\n     * <p class=\"note\">Note that {@link #afterHookedMethod} will be called in reversed order, i.e.\n     * the callback with the highest priority will be called last. This way, the callback has the\n     * final control over the return value. {@link #beforeHookedMethod} is called as usual, i.e.\n     * highest priority first.\n     *\n     * @param priority See {@link XCallback#priority}.\n     */\n    public XC_MethodHook(int priority) {\n        super(priority);\n    }\n\n    /**\n     * Called before the invocation of the method.\n     *\n     * <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}\n     * to prevent the original method from being called.\n     *\n     * <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.\n     *\n     * @param param Information about the method call.\n     * @throws Throwable Everything the callback throws is caught and logged.\n     */\n    protected void beforeHookedMethod(MethodHookParam<?> param) throws Throwable {\n    }\n\n    public void callBeforeHookedMethod(MethodHookParam<?> param) throws Throwable {\n        beforeHookedMethod(param);\n    }\n\n    /**\n     * Called after the invocation of the method.\n     *\n     * <p>You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}\n     * to modify the return value of the original method.\n     *\n     * <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.\n     *\n     * @param param Information about the method call.\n     * @throws Throwable Everything the callback throws is caught and logged.\n     */\n    protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {\n    }\n\n    public void callAfterHookedMethod(MethodHookParam<?> param) throws Throwable {\n        afterHookedMethod(param);\n    }\n\n    /**\n     * Wraps information about the method call and allows to influence it.\n     */\n    public static final class MethodHookParam<T extends Executable> extends XCallback.Param {\n        /**\n         * @hide\n         */\n        @SuppressWarnings(\"deprecation\")\n        public MethodHookParam() {\n            super();\n        }\n\n        /**\n         * The hooked method/constructor.\n         */\n        public Member method;\n\n        /**\n         * The {@code this} reference for an instance method, or {@code null} for static methods.\n         */\n        public Object thisObject;\n\n        /**\n         * Arguments to the method call.\n         */\n        public Object[] args;\n\n        public Object result = null;\n        public Throwable throwable = null;\n        public boolean returnEarly = false;\n\n        private final HashMap<String, Object> extras = new HashMap<>();\n\n        /**\n         * Returns the result of the method call.\n         */\n        public Object getResult() {\n            return result;\n        }\n\n        /**\n         * Modify the result of the method call.\n         *\n         * <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.\n         */\n        public void setResult(Object result) {\n            this.result = result;\n            this.throwable = null;\n            this.returnEarly = true;\n        }\n\n        /**\n         * Returns the {@link Throwable} thrown by the method, or {@code null}.\n         */\n        public Throwable getThrowable() {\n            return throwable;\n        }\n\n        /**\n         * Returns true if an exception was thrown by the method.\n         */\n        public boolean hasThrowable() {\n            return throwable != null;\n        }\n\n        /**\n         * Modify the exception thrown of the method call.\n         *\n         * <p>If called from {@link #beforeHookedMethod}, it prevents the call to the original method.\n         */\n        public void setThrowable(Throwable throwable) {\n            this.throwable = throwable;\n            this.result = null;\n            this.returnEarly = true;\n        }\n\n        /**\n         * Returns the result of the method call, or throws the Throwable caused by it.\n         */\n        public Object getResultOrThrowable() throws Throwable {\n            if (throwable != null)\n                throw throwable;\n            return result;\n        }\n    }\n\n    /**\n     * An object with which the method/constructor can be unhooked.\n     */\n    public class Unhook implements IXUnhook<XC_MethodHook> {\n        private final Member hookMethod;\n\n        /*package*/ Unhook(Member hookMethod) {\n            this.hookMethod = hookMethod;\n        }\n\n        /**\n         * Returns the method/constructor that has been hooked.\n         */\n        public Member getHookedMethod() {\n            return hookMethod;\n        }\n\n        @Override\n        public XC_MethodHook getCallback() {\n            return XC_MethodHook.this;\n        }\n\n        @SuppressWarnings(\"deprecation\")\n        @Override\n        public void unhook() {\n            XposedBridge.unhookMethod(hookMethod, XC_MethodHook.this);\n        }\n\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/XC_MethodReplacement.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport de.robv.android.xposed.callbacks.XCallback;\n\n/**\n * A special case of {@link XC_MethodHook} which completely replaces the original method.\n */\npublic abstract class XC_MethodReplacement extends XC_MethodHook {\n    /**\n     * Creates a new callback with default priority.\n     */\n    public XC_MethodReplacement() {\n        super();\n    }\n\n    /**\n     * Creates a new callback with a specific priority.\n     *\n     * @param priority See {@link XCallback#priority}.\n     */\n    public XC_MethodReplacement(int priority) {\n        super(priority);\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        try {\n            Object result = replaceHookedMethod(param);\n            param.setResult(result);\n        } catch (Throwable t) {\n            param.setThrowable(t);\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    @SuppressWarnings(\"EmptyMethod\")\n    protected final void afterHookedMethod(MethodHookParam param) throws Throwable {\n    }\n\n    /**\n     * Shortcut for replacing a method completely. Whatever is returned/thrown here is taken\n     * instead of the result of the original method (which will not be called).\n     *\n     * <p>Note that implementations shouldn't call {@code super(param)}, it's not necessary.\n     *\n     * @param param Information about the method call.\n     * @throws Throwable Anything that is thrown by the callback will be passed on to the original caller.\n     */\n    @SuppressWarnings(\"UnusedParameters\")\n    protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;\n\n    /**\n     * Predefined callback that skips the method without replacements.\n     */\n    public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST * 2) {\n        @Override\n        protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {\n            return null;\n        }\n    };\n\n    /**\n     * Creates a callback which always returns a specific value.\n     *\n     * @param result The value that should be returned to callers of the hooked method.\n     */\n    public static XC_MethodReplacement returnConstant(final Object result) {\n        return returnConstant(PRIORITY_DEFAULT, result);\n    }\n\n    /**\n     * Like {@link #returnConstant(Object)}, but allows to specify a priority for the callback.\n     *\n     * @param priority See {@link XCallback#priority}.\n     * @param result   The value that should be returned to callers of the hooked method.\n     */\n    public static XC_MethodReplacement returnConstant(int priority, final Object result) {\n        return new XC_MethodReplacement(priority) {\n            @Override\n            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {\n                return result;\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/XSharedPreferences.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 - 2022 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.os.Environment;\nimport android.preference.PreferenceManager;\n\nimport com.android.internal.util.XmlUtils;\n\nimport org.lsposed.lspd.core.BuildConfig;\nimport org.lsposed.lspd.util.MetaDataReader;\nimport org.lsposed.lspd.util.Utils.Log;\nimport org.xmlpull.v1.XmlPullParserException;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.AccessDeniedException;\nimport java.nio.file.ClosedWatchServiceException;\nimport java.nio.file.Path;\nimport java.nio.file.StandardWatchEventKinds;\nimport java.nio.file.WatchEvent;\nimport java.nio.file.WatchKey;\nimport java.nio.file.WatchService;\nimport java.security.MessageDigest;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport de.robv.android.xposed.services.FileResult;\n\n/**\n * This class is basically the same as SharedPreferencesImpl from AOSP, but\n * read-only and without listeners support. Instead, it is made to be\n * compatible with all ROMs.\n */\npublic final class XSharedPreferences implements SharedPreferences {\n    private static final String TAG = \"XSharedPreferences\";\n    private static final HashMap<WatchKey, PrefsData> sWatcherKeyInstances = new HashMap<>();\n    private static final Object sContent = new Object();\n    private static Thread sWatcherDaemon = null;\n    private static WatchService sWatcher;\n\n    private final HashMap<OnSharedPreferenceChangeListener, Object> mListeners = new HashMap<>();\n    private final File mFile;\n    private final String mFilename;\n    private Map<String, Object> mMap;\n    private boolean mLoaded = false;\n    private long mLastModified;\n    private long mFileSize;\n    private WatchKey mWatchKey;\n\n    private static void initWatcherDaemon() {\n        sWatcherDaemon = new Thread() {\n            @Override\n            public void run() {\n                if (BuildConfig.DEBUG) Log.d(TAG, \"Watcher daemon thread started\");\n                while (true) {\n                    WatchKey key;\n                    try {\n                        key = sWatcher.take();\n                    } catch (ClosedWatchServiceException ignored) {\n                        if (BuildConfig.DEBUG) Log.d(TAG, \"Watcher daemon thread finished\");\n                        sWatcher = null;\n                        return;\n                    } catch (InterruptedException ignored) {\n                        return;\n                    }\n                    for (WatchEvent<?> event : key.pollEvents()) {\n                        WatchEvent.Kind<?> kind = event.kind();\n                        if (kind == StandardWatchEventKinds.OVERFLOW) {\n                            continue;\n                        }\n                        Path dir = (Path) key.watchable();\n                        Path path = dir.resolve((Path) event.context());\n                        String pathStr = path.toString();\n                        if (BuildConfig.DEBUG)\n                            Log.v(TAG, \"File \" + path.toString() + \" event: \" + kind.name());\n                        // We react to both real and backup files due to rare race conditions\n                        if (pathStr.endsWith(\".bak\")) {\n                            if (kind != StandardWatchEventKinds.ENTRY_DELETE) {\n                                continue;\n                            }\n                        } else if (SELinuxHelper.getAppDataFileService().checkFileExists(pathStr + \".bak\")) {\n                            continue;\n                        }\n                        PrefsData data = sWatcherKeyInstances.get(key);\n                        if (data != null && data.hasChanged()) {\n                            for (OnSharedPreferenceChangeListener l : data.mPrefs.mListeners.keySet()) {\n                                try {\n                                    l.onSharedPreferenceChanged(data.mPrefs, null);\n                                } catch (Throwable t) {\n                                    if (BuildConfig.DEBUG)\n                                        Log.e(TAG, \"Fail in preference change listener\", t);\n                                }\n                            }\n                        }\n                    }\n                    key.reset();\n                }\n            }\n        };\n        sWatcherDaemon.setName(TAG + \"-Daemon\");\n        sWatcherDaemon.setDaemon(true);\n        sWatcherDaemon.start();\n    }\n\n    /**\n     * Read settings from the specified file.\n     *\n     * @param prefFile The file to read the preferences from.\n     */\n    public XSharedPreferences(File prefFile) {\n        mFile = prefFile;\n        mFilename = prefFile.getAbsolutePath();\n        init();\n    }\n\n    /**\n     * Read settings from the default preferences for a package.\n     * These preferences are returned by {@link PreferenceManager#getDefaultSharedPreferences}.\n     *\n     * @param packageName The package name.\n     */\n    public XSharedPreferences(String packageName) {\n        this(packageName, packageName + \"_preferences\");\n    }\n\n    /**\n     * Read settings from a custom preferences file for a package.\n     * These preferences are returned by {@link Context#getSharedPreferences(String, int)}.\n     *\n     * @param packageName  The package name.\n     * @param prefFileName The file name without \".xml\".\n     */\n    public XSharedPreferences(String packageName, String prefFileName) {\n        boolean newModule = false;\n        var m = XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty());\n        if (m.isPresent()) {\n            boolean isModule = false;\n            int xposedminversion = -1;\n            boolean xposedsharedprefs = false;\n            try {\n                Map<String, Object> metaData = MetaDataReader.getMetaData(new File(m.get()));\n                isModule = metaData.containsKey(\"xposedminversion\");\n                if (isModule) {\n                    Object minVersionRaw = metaData.get(\"xposedminversion\");\n                    if (minVersionRaw instanceof Integer) {\n                        xposedminversion = (Integer) minVersionRaw;\n                    } else if (minVersionRaw instanceof String) {\n                        xposedminversion = MetaDataReader.extractIntPart((String) minVersionRaw);\n                    }\n                    xposedsharedprefs = metaData.containsKey(\"xposedsharedprefs\");\n                }\n            } catch (NumberFormatException | IOException e) {\n                Log.w(TAG, \"Apk parser fails: \" + e);\n            }\n            newModule = isModule && (xposedminversion > 92 || xposedsharedprefs);\n        }\n        if (newModule) {\n            mFile = new File(serviceClient.getPrefsPath(packageName), prefFileName + \".xml\");\n        } else {\n            mFile = new File(Environment.getDataDirectory(), \"data/\" + packageName + \"/shared_prefs/\" + prefFileName + \".xml\");\n        }\n        mFilename = mFile.getAbsolutePath();\n        init();\n    }\n\n    private void tryRegisterWatcher() {\n        if (mWatchKey != null && mWatchKey.isValid()) {\n            return;\n        }\n\n        synchronized (sWatcherKeyInstances) {\n            Path path = mFile.toPath();\n            try {\n                if (sWatcher == null) {\n                    sWatcher = new File(serviceClient.getPrefsPath(\"\")).toPath().getFileSystem().newWatchService();\n                    if (BuildConfig.DEBUG) Log.d(TAG, \"Created WatchService instance\");\n                }\n                mWatchKey = path.getParent().register(sWatcher, StandardWatchEventKinds.ENTRY_CREATE,\n                        StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);\n                sWatcherKeyInstances.put(mWatchKey, new PrefsData(this));\n                if (sWatcherDaemon == null || !sWatcherDaemon.isAlive()) {\n                    initWatcherDaemon();\n                }\n                if (BuildConfig.DEBUG)\n                    Log.d(TAG, \"tryRegisterWatcher: registered file watcher for \" + path);\n            } catch (AccessDeniedException accDeniedEx) {\n                if (BuildConfig.DEBUG) Log.e(TAG, \"tryRegisterWatcher: access denied to \" + path);\n            } catch (Exception e) {\n                Log.e(TAG, \"tryRegisterWatcher: failed to register file watcher\", e);\n            }\n        }\n    }\n\n    private void tryUnregisterWatcher() {\n        synchronized (sWatcherKeyInstances) {\n            if (mWatchKey != null) {\n                sWatcherKeyInstances.remove(mWatchKey);\n                mWatchKey.cancel();\n                mWatchKey = null;\n            }\n            boolean atLeastOneValid = false;\n            for (WatchKey key : sWatcherKeyInstances.keySet()) {\n                atLeastOneValid |= key.isValid();\n            }\n            if (!atLeastOneValid) {\n                try {\n                    sWatcher.close();\n                } catch (Exception ignore) {\n                }\n            }\n        }\n    }\n\n    private void init() {\n        startLoadFromDisk();\n    }\n\n    private static long tryGetFileSize(String filename) {\n        try {\n            return SELinuxHelper.getAppDataFileService().getFileSize(filename);\n        } catch (IOException ignored) {\n            return 0;\n        }\n    }\n\n    private static byte[] tryGetFileHash(String filename) {\n        try {\n            MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            try (InputStream is = SELinuxHelper.getAppDataFileService().getFileInputStream(filename)) {\n                byte[] buf = new byte[4096];\n                int read;\n                while ((read = is.read(buf)) != -1) {\n                    md.update(buf, 0, read);\n                }\n            }\n            return md.digest();\n        } catch (Exception ignored) {\n            return new byte[0];\n        }\n    }\n\n    /**\n     * Tries to make the preferences file world-readable.\n     *\n     * <p><strong>Warning:</strong> This is only meant to work around permission \"fix\" functions that are part\n     * of some recoveries. It doesn't replace the need to open preferences with {@code MODE_WORLD_READABLE}\n     * in the module's UI code. Otherwise, Android will set stricter permissions again during the next save.\n     *\n     * <p>This will only work if executed as root (e.g. {@code initZygote()}) and only if SELinux is disabled.\n     *\n     * @return {@code true} in case the file could be made world-readable.\n     */\n    @SuppressLint(\"SetWorldReadable\")\n    public boolean makeWorldReadable() {\n        if (!SELinuxHelper.getAppDataFileService().hasDirectFileAccess())\n            return false; // It doesn't make much sense to make the file readable if we wouldn't be able to access it anyway.\n\n        if (!mFile.exists()) // Just in case - the file should never be created if it doesn't exist.\n            return false;\n\n        if (!mFile.setReadable(true, false))\n            return false;\n\n        // Watcher service needs read access to parent directory (looks like execute is not enough)\n        if (mFile.getParentFile() != null) {\n            mFile.getParentFile().setReadable(true, false);\n        }\n\n        if (!mListeners.isEmpty()) {\n            tryRegisterWatcher();\n        }\n\n        return true;\n    }\n\n    /**\n     * Returns the file that is backing these preferences.\n     *\n     * <p><strong>Warning:</strong> The file might not be accessible directly.\n     */\n    public File getFile() {\n        return mFile;\n    }\n\n    private void startLoadFromDisk() {\n        synchronized (this) {\n            mLoaded = false;\n        }\n        new Thread(\"XSharedPreferences-load\") {\n            @Override\n            public void run() {\n                synchronized (XSharedPreferences.this) {\n                    loadFromDiskLocked();\n                }\n            }\n        }.start();\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private void loadFromDiskLocked() {\n        if (mLoaded) {\n            return;\n        }\n\n        Map map = null;\n        FileResult result = null;\n        try {\n            result = SELinuxHelper.getAppDataFileService().getFileInputStream(mFilename, mFileSize, mLastModified);\n            if (result.stream != null) {\n                map = XmlUtils.readMapXml(result.stream);\n                result.stream.close();\n            } else {\n                // The file is unchanged, keep the current values\n                map = mMap;\n            }\n        } catch (XmlPullParserException e) {\n            Log.w(TAG, \"getSharedPreferences failed for: \" + mFilename, e);\n        } catch (FileNotFoundException ignored) {\n            // SharedPreferencesImpl has a canRead() check, so it doesn't log anything in case the file doesn't exist\n        } catch (IOException e) {\n            Log.w(TAG, \"getSharedPreferences failed for: \" + mFilename, e);\n        } finally {\n            if (result != null && result.stream != null) {\n                try {\n                    result.stream.close();\n                } catch (RuntimeException rethrown) {\n                    throw rethrown;\n                } catch (Exception ignored) {\n                }\n            }\n        }\n\n        mLoaded = true;\n        if (map != null) {\n            mMap = map;\n            mLastModified = result.mtime;\n            mFileSize = result.size;\n        } else {\n            mMap = new HashMap<>();\n        }\n        notifyAll();\n    }\n\n    /**\n     * Reload the settings from file if they have changed.\n     *\n     * <p><strong>Warning:</strong> With enforcing SELinux, this call might be quite expensive.\n     */\n    public synchronized void reload() {\n        if (hasFileChanged()) {\n            init();\n        }\n    }\n\n    /**\n     * Check whether the file has changed since the last time it has been loaded.\n     *\n     * <p><strong>Warning:</strong> With enforcing SELinux, this call might be quite expensive.\n     */\n    public synchronized boolean hasFileChanged() {\n        try {\n            FileResult result = SELinuxHelper.getAppDataFileService().statFile(mFilename);\n            return mLastModified != result.mtime || mFileSize != result.size;\n        } catch (FileNotFoundException ignored) {\n            // SharedPreferencesImpl doesn't log anything in case the file doesn't exist\n            return true;\n        } catch (IOException e) {\n            Log.w(TAG, \"hasFileChanged\", e);\n            return true;\n        }\n    }\n\n    private void awaitLoadedLocked() {\n        while (!mLoaded) {\n            try {\n                wait();\n            } catch (InterruptedException unused) {\n            }\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public Map<String, ?> getAll() {\n        synchronized (this) {\n            awaitLoadedLocked();\n            return new HashMap<>(mMap);\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public String getString(String key, String defValue) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            String v = (String) mMap.get(key);\n            return v != null ? v : defValue;\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Set<String> getStringSet(String key, Set<String> defValues) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            Set<String> v = (Set<String>) mMap.get(key);\n            return v != null ? v : defValues;\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public int getInt(String key, int defValue) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            Integer v = (Integer) mMap.get(key);\n            return v != null ? v : defValue;\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public long getLong(String key, long defValue) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            Long v = (Long) mMap.get(key);\n            return v != null ? v : defValue;\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public float getFloat(String key, float defValue) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            Float v = (Float) mMap.get(key);\n            return v != null ? v : defValue;\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public boolean getBoolean(String key, boolean defValue) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            Boolean v = (Boolean) mMap.get(key);\n            return v != null ? v : defValue;\n        }\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public boolean contains(String key) {\n        synchronized (this) {\n            awaitLoadedLocked();\n            return mMap.containsKey(key);\n        }\n    }\n\n    /**\n     * @deprecated Not supported by this implementation.\n     */\n    @Deprecated\n    @Override\n    public Editor edit() {\n        throw new UnsupportedOperationException(\"read-only implementation\");\n    }\n\n    /**\n     * Registers a callback to be invoked when a change happens to a preference file.<br>\n     * Note that it is not possible to determine which preference changed exactly and thus\n     * preference key in callback invocation will always be null.\n     *\n     * @param listener The callback that will run.\n     * @see #unregisterOnSharedPreferenceChangeListener\n     */\n    @Override\n    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        if (listener == null)\n            throw new IllegalArgumentException(\"listener cannot be null\");\n\n        synchronized (this) {\n            if (mListeners.put(listener, sContent) == null) {\n                tryRegisterWatcher();\n            }\n        }\n    }\n\n    /**\n     * Unregisters a previous callback.\n     *\n     * @param listener The callback that should be unregistered.\n     * @see #registerOnSharedPreferenceChangeListener\n     */\n    @Override\n    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        synchronized (this) {\n            if (mListeners.remove(listener) != null && mListeners.isEmpty()) {\n                tryUnregisterWatcher();\n            }\n        }\n    }\n\n    private static class PrefsData {\n        public final XSharedPreferences mPrefs;\n        private long mSize;\n        private byte[] mHash;\n\n        public PrefsData(XSharedPreferences prefs) {\n            mPrefs = prefs;\n            mSize = tryGetFileSize(prefs.mFilename);\n            mHash = tryGetFileHash(prefs.mFilename);\n        }\n\n        public boolean hasChanged() {\n            long size = tryGetFileSize(mPrefs.mFilename);\n            if (size < 1) {\n                if (BuildConfig.DEBUG) Log.d(TAG, \"Ignoring empty prefs file\");\n                return false;\n            }\n            if (size != mSize) {\n                mSize = size;\n                mHash = tryGetFileHash(mPrefs.mFilename);\n                if (BuildConfig.DEBUG) Log.d(TAG, \"Prefs file size changed\");\n                return true;\n            }\n            byte[] hash = tryGetFileHash(mPrefs.mFilename);\n            if (!Arrays.equals(hash, mHash)) {\n                mHash = hash;\n                if (BuildConfig.DEBUG) Log.d(TAG, \"Prefs file hash changed\");\n                return true;\n            }\n            if (BuildConfig.DEBUG) Log.d(TAG, \"Prefs file not changed\");\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/XposedBridge.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 - 2022 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport android.app.ActivityThread;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.util.Log;\n\nimport org.lsposed.lspd.impl.LSPosedBridge;\nimport org.lsposed.lspd.impl.LSPosedHookCallback;\nimport org.matrix.vector.nativebridge.HookBridge;\nimport org.matrix.vector.nativebridge.ResourcesHook;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport de.robv.android.xposed.callbacks.XC_InitPackageResources;\nimport de.robv.android.xposed.callbacks.XC_LoadPackage;\nimport io.github.libxposed.api.XposedInterface;\n\n/**\n * This class contains most of Xposed's central logic, such as initialization and callbacks used by\n * the native side. It also includes methods to add new hooks.\n */\npublic final class XposedBridge {\n    /**\n     * The system class loader which can be used to locate Android framework classes.\n     * Application classes cannot be retrieved from it.\n     *\n     * @see ClassLoader#getSystemClassLoader\n     */\n    public static final ClassLoader BOOTCLASSLOADER = XposedBridge.class.getClassLoader();\n\n    /**\n     * @hide\n     */\n    public static final String TAG = \"LSPosed-Bridge\";\n\n    /**\n     * @deprecated Use {@link #getXposedVersion()} instead.\n     */\n    @Deprecated\n    public static int XPOSED_BRIDGE_VERSION;\n\n    private static final Object[] EMPTY_ARRAY = new Object[0];\n\n    // built-in handlers\n    public static final CopyOnWriteArraySet<XC_LoadPackage> sLoadedPackageCallbacks = new CopyOnWriteArraySet<>();\n    /*package*/ static final CopyOnWriteArraySet<XC_InitPackageResources> sInitPackageResourcesCallbacks = new CopyOnWriteArraySet<>();\n\n    private XposedBridge() {\n    }\n\n    public static volatile ClassLoader dummyClassLoader = null;\n\n    public static void initXResources() {\n        if (dummyClassLoader != null) {\n            return;\n        }\n        try {\n            Resources res = Resources.getSystem();\n            Class<?> resClass = res.getClass();\n            Class<?> taClass = TypedArray.class;\n            try {\n                try {\n                    TypedArray ta = res.obtainTypedArray(res.getIdentifier(\n                            \"preloaded_drawables\", \"array\", \"android\"));\n                    taClass = ta.getClass();\n                    ta.recycle();\n                } catch (NullPointerException npe) {\n                    // For ZUI devices, the creation of TypedArray needs to check the configuration\n                    // from ActivityThread.currentActivityThread. However, we do not have a valid\n                    // ActivityThread for now and the call will throw an NPE. Luckily they check the\n                    // nullability of the result configuration. So we hereby set a dummy\n                    // ActivityThread to bypass such a situation.\n                    var fake = XposedHelpers.newInstance(ActivityThread.class);\n                    XposedHelpers.setStaticObjectField(ActivityThread.class, \"sCurrentActivityThread\", fake);\n                    try {\n                        TypedArray ta = res.obtainTypedArray(res.getIdentifier(\n                                \"preloaded_drawables\", \"array\", \"android\"));\n                        taClass = ta.getClass();\n                        ta.recycle();\n                    } finally {\n                        XposedHelpers.setStaticObjectField(ActivityThread.class, \"sCurrentActivityThread\", null);\n                    }\n                }\n            } catch (Resources.NotFoundException nfe) {\n                XposedBridge.log(nfe);\n            }\n            ResourcesHook.makeInheritable(resClass);\n            ResourcesHook.makeInheritable(taClass);\n            ClassLoader myCL = XposedBridge.class.getClassLoader();\n            assert myCL != null;\n            dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass.getName(), taClass.getName());\n            dummyClassLoader.loadClass(\"xposed.dummy.XResourcesSuperClass\");\n            dummyClassLoader.loadClass(\"xposed.dummy.XTypedArraySuperClass\");\n            XposedHelpers.setObjectField(myCL, \"parent\", dummyClassLoader);\n        } catch (Throwable throwable) {\n            XposedBridge.log(throwable);\n            XposedInit.disableResources = true;\n        }\n    }\n\n    /**\n     * Returns the currently installed version of the Xposed framework.\n     */\n    public static int getXposedVersion() {\n        return XposedInterface.API;\n    }\n\n    /**\n     * Writes a message to the Xposed modules log.\n     *\n     * <p class=\"warning\"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.\n     * If you want to write information/debug messages, use logcat.\n     *\n     * @param text The log message.\n     */\n    public synchronized static void log(String text) {\n        Log.i(TAG, text);\n    }\n\n    /**\n     * Logs a stack trace to the Xposed modules log.\n     *\n     * <p class=\"warning\"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.\n     * If you want to write information/debug messages, use logcat.\n     *\n     * @param t The Throwable object for the stack trace.\n     */\n    public synchronized static void log(Throwable t) {\n        String logStr = Log.getStackTraceString(t);\n        Log.e(TAG, logStr);\n    }\n\n    /**\n     * Deoptimize a method to avoid callee being inlined.\n     *\n     * @param deoptimizedMethod The method to deoptmize. Generally it should be a caller of a method\n     *                          that is inlined.\n     */\n    public static void deoptimizeMethod(Member deoptimizedMethod) {\n        if (!(deoptimizedMethod instanceof Executable)) {\n            throw new IllegalArgumentException(\"Only methods and constructors can be deoptimized: \" + deoptimizedMethod);\n        } else if (Modifier.isAbstract(deoptimizedMethod.getModifiers())) {\n            throw new IllegalArgumentException(\"Cannot deoptimize abstract methods: \" + deoptimizedMethod);\n        } else if (Proxy.isProxyClass(deoptimizedMethod.getDeclaringClass())) {\n            throw new IllegalArgumentException(\"Cannot deoptimize methods from proxy class: \" + deoptimizedMethod);\n        }\n        HookBridge.deoptimizeMethod((Executable) deoptimizedMethod);\n    }\n\n    /**\n     * Hook any method (or constructor) with the specified callback. See below for some wrappers\n     * that make it easier to find a method/constructor in one step.\n     *\n     * @param hookMethod The method to be hooked.\n     * @param callback   The callback to be executed when the hooked method is called.\n     * @return An object that can be used to remove the hook.\n     * @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)\n     * @see XposedHelpers#findAndHookMethod(Class, String, Object...)\n     * @see #hookAllMethods\n     * @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)\n     * @see XposedHelpers#findAndHookConstructor(Class, Object...)\n     * @see #hookAllConstructors\n     */\n    public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {\n        if (!(hookMethod instanceof Executable)) {\n            throw new IllegalArgumentException(\"Only methods and constructors can be hooked: \" + hookMethod);\n        } else if (Modifier.isAbstract(hookMethod.getModifiers())) {\n            throw new IllegalArgumentException(\"Cannot hook abstract methods: \" + hookMethod);\n        } else if (hookMethod.getDeclaringClass().getClassLoader() == XposedBridge.class.getClassLoader()) {\n            throw new IllegalArgumentException(\"Do not allow hooking inner methods\");\n        } else if (hookMethod.getDeclaringClass() == Method.class && hookMethod.getName().equals(\"invoke\")) {\n            throw new IllegalArgumentException(\"Cannot hook Method.invoke\");\n        }\n\n        if (callback == null) {\n            throw new IllegalArgumentException(\"callback should not be null!\");\n        }\n\n        if (!HookBridge.hookMethod(false, (Executable) hookMethod, LSPosedBridge.NativeHooker.class, callback.priority, callback)) {\n            log(\"Failed to hook \" + hookMethod);\n            return null;\n        }\n\n        return callback.new Unhook(hookMethod);\n    }\n\n    /**\n     * Removes the callback for a hooked method/constructor.\n     *\n     * @param hookMethod The method for which the callback should be removed.\n     * @param callback   The reference to the callback as specified in {@link #hookMethod}.\n     * @deprecated Use {@link XC_MethodHook.Unhook#unhook} instead. An instance of the {@code Unhook}\n     * class is returned when you hook the method.\n     */\n    @Deprecated\n    public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {\n        if (hookMethod instanceof Executable) {\n            HookBridge.unhookMethod(false, (Executable) hookMethod, callback);\n        }\n    }\n\n    /**\n     * Hooks all methods with a certain name that were declared in the specified class. Inherited\n     * methods and constructors are not considered. For constructors, use\n     * {@link #hookAllConstructors} instead.\n     *\n     * @param hookClass  The class to check for declared methods.\n     * @param methodName The name of the method(s) to hook.\n     * @param callback   The callback to be executed when the hooked methods are called.\n     * @return A set containing one object for each found method which can be used to unhook it.\n     */\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {\n        Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();\n        for (Member method : hookClass.getDeclaredMethods())\n            if (method.getName().equals(methodName))\n                unhooks.add(hookMethod(method, callback));\n        return unhooks;\n    }\n\n    /**\n     * Hook all constructors of the specified class.\n     *\n     * @param hookClass The class to check for constructors.\n     * @param callback  The callback to be executed when the hooked constructors are called.\n     * @return A set containing one object for each found constructor which can be used to unhook it.\n     */\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {\n        Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();\n        for (Member constructor : hookClass.getDeclaredConstructors())\n            unhooks.add(hookMethod(constructor, callback));\n        return unhooks;\n    }\n\n    /**\n     * Adds a callback to be executed when an app (\"Android package\") is loaded.\n     *\n     * <p class=\"note\">You probably don't need to call this. Simply implement {@link IXposedHookLoadPackage}\n     * in your module class and Xposed will take care of registering it as a callback.\n     *\n     * @param callback The callback to be executed.\n     * @hide\n     */\n    public static void hookLoadPackage(XC_LoadPackage callback) {\n        synchronized (sLoadedPackageCallbacks) {\n            sLoadedPackageCallbacks.add(callback);\n        }\n    }\n\n    /**\n     * Adds a callback to be executed when the resources for an app are initialized.\n     *\n     * <p class=\"note\">You probably don't need to call this. Simply implement {@link IXposedHookInitPackageResources}\n     * in your module class and Xposed will take care of registering it as a callback.\n     *\n     * @param callback The callback to be executed.\n     * @hide\n     */\n    public static void hookInitPackageResources(XC_InitPackageResources callback) {\n        synchronized (sInitPackageResourcesCallbacks) {\n            sInitPackageResourcesCallbacks.add(callback);\n        }\n    }\n\n    /**\n     * Basically the same as {@link Method#invoke}, but calls the original method\n     * as it was before the interception by Xposed. Also, access permissions are not checked.\n     *\n     * <p class=\"caution\">There are very few cases where this method is needed. A common mistake is\n     * to replace a method and then invoke the original one based on dynamic conditions. This\n     * creates overhead and skips further hooks by other modules. Instead, just hook (don't replace)\n     * the method and call {@code param.setResult(null)} in {@link XC_MethodHook#beforeHookedMethod}\n     * if the original method should be skipped.\n     *\n     * @param method     The method to be called.\n     * @param thisObject For non-static calls, the \"this\" pointer, otherwise {@code null}.\n     * @param args       Arguments for the method call as Object[] array.\n     * @return The result returned from the invoked method.\n     * @throws NullPointerException      if {@code receiver == null} for a non-static method\n     * @throws IllegalAccessException    if this method is not accessible (see {@link AccessibleObject})\n     * @throws IllegalArgumentException  if the number of arguments doesn't match the number of parameters, the receiver\n     *                                   is incompatible with the declaring class, or an argument could not be unboxed\n     *                                   or converted by a widening conversion to the corresponding parameter type\n     * @throws InvocationTargetException if an exception was thrown by the invoked method\n     */\n    public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)\n            throws Throwable {\n        if (args == null) {\n            args = EMPTY_ARRAY;\n        }\n\n        if (!(method instanceof Executable)) {\n            throw new IllegalArgumentException(\"method must be of type Method or Constructor\");\n        }\n\n        return HookBridge.invokeOriginalMethod((Executable) method, thisObject, args);\n    }\n\n    /**\n     * @hide\n     */\n    public static final class CopyOnWriteSortedSet<E> {\n        private transient volatile Object[] elements = EMPTY_ARRAY;\n\n        @SuppressWarnings(\"UnusedReturnValue\")\n        public synchronized boolean add(E e) {\n            int index = indexOf(e);\n            if (index >= 0)\n                return false;\n\n            Object[] newElements = new Object[elements.length + 1];\n            System.arraycopy(elements, 0, newElements, 0, elements.length);\n            newElements[elements.length] = e;\n            Arrays.sort(newElements);\n            elements = newElements;\n            return true;\n        }\n\n        @SuppressWarnings(\"UnusedReturnValue\")\n        public synchronized boolean remove(E e) {\n            int index = indexOf(e);\n            if (index == -1)\n                return false;\n\n            Object[] newElements = new Object[elements.length - 1];\n            System.arraycopy(elements, 0, newElements, 0, index);\n            System.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);\n            elements = newElements;\n            return true;\n        }\n\n        private int indexOf(Object o) {\n            for (int i = 0; i < elements.length; i++) {\n                if (o.equals(elements[i]))\n                    return i;\n            }\n            return -1;\n        }\n\n        public Object[] getSnapshot() {\n            return elements;\n        }\n\n        public <T> T[] getSnapshot(T[] a) {\n            var snapshot = getSnapshot();\n            return (T[]) Arrays.copyOf(snapshot, snapshot.length, a.getClass());\n        }\n\n        public synchronized void clear() {\n            elements = EMPTY_ARRAY;\n        }\n    }\n\n    public static class LegacyApiSupport<T extends Executable> {\n        private final XC_MethodHook.MethodHookParam<T> param;\n        private final LSPosedHookCallback<T> callback;\n        private final Object[] snapshot;\n\n        private int beforeIdx;\n\n        public LegacyApiSupport(LSPosedHookCallback<T> callback, Object[] legacySnapshot) {\n            this.param = new XC_MethodHook.MethodHookParam<>();\n            this.callback = callback;\n            this.snapshot = legacySnapshot;\n        }\n\n        public void handleBefore() {\n            syncronizeApi(param, callback, true);\n            for (beforeIdx = 0; beforeIdx < snapshot.length; beforeIdx++) {\n                try {\n                    var cb = (XC_MethodHook) snapshot[beforeIdx];\n                    cb.beforeHookedMethod(param);\n                } catch (Throwable t) {\n                    XposedBridge.log(t);\n\n                    // reset result (ignoring what the unexpectedly exiting callback did)\n                    param.setResult(null);\n                    param.returnEarly = false;\n                    continue;\n                }\n\n                if (param.returnEarly) {\n                    // skip remaining \"before\" callbacks and corresponding \"after\" callbacks\n                    beforeIdx++;\n                    break;\n                }\n            }\n            syncronizeApi(param, callback, false);\n        }\n\n        public void handleAfter() {\n            syncronizeApi(param, callback, true);\n            for (int afterIdx = beforeIdx - 1; afterIdx >= 0; afterIdx--) {\n                Object lastResult = param.getResult();\n                Throwable lastThrowable = param.getThrowable();\n                try {\n                    var cb = (XC_MethodHook) snapshot[afterIdx];\n                    cb.afterHookedMethod(param);\n                } catch (Throwable t) {\n                    XposedBridge.log(t);\n\n                    // reset to last result (ignoring what the unexpectedly exiting callback did)\n                    if (lastThrowable == null) {\n                        param.setResult(lastResult);\n                    } else {\n                        param.setThrowable(lastThrowable);\n                    }\n                }\n            }\n            syncronizeApi(param, callback, false);\n        }\n\n        private void syncronizeApi(XC_MethodHook.MethodHookParam<T> param, LSPosedHookCallback<T> callback, boolean forward) {\n            if (forward) {\n                param.method = callback.method;\n                param.thisObject = callback.thisObject;\n                param.args = callback.args;\n                param.result = callback.result;\n                param.throwable = callback.throwable;\n                param.returnEarly = callback.isSkipped;\n            } else {\n                callback.method = param.method;\n                callback.thisObject = param.thisObject;\n                callback.args = param.args;\n                callback.result = param.result;\n                callback.throwable = param.throwable;\n                callback.isSkipped = param.returnEarly;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/XposedHelpers.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport android.content.res.AssetManager;\nimport android.content.res.Resources;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.apache.commons.lang3.ClassUtilsX;\nimport org.apache.commons.lang3.reflect.MemberUtilsX;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.WeakHashMap;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Helpers that simplify hooking and calling methods/constructors, getting and settings fields, ...\n */\npublic final class XposedHelpers {\n    private XposedHelpers() {\n    }\n\n    private static final ConcurrentHashMap<MemberCacheKey.Field, Optional<Field>> fieldCache = new ConcurrentHashMap<>();\n    private static final ConcurrentHashMap<MemberCacheKey.Method, Optional<Method>> methodCache = new ConcurrentHashMap<>();\n    private static final ConcurrentHashMap<MemberCacheKey.Constructor, Optional<Constructor<?>>> constructorCache = new ConcurrentHashMap<>();\n    private static final WeakHashMap<Object, HashMap<String, Object>> additionalFields = new WeakHashMap<>();\n    private static final HashMap<String, ThreadLocal<AtomicInteger>> sMethodDepth = new HashMap<>();\n\n    /**\n     * Note that we use object key instead of string here, because string calculation will lose all\n     * the benefits of 'HashMap', this is basically the solution of performance traps.\n     * <p>\n     * So in fact we only need to use the structural comparison results of the reflection object.\n     *\n     * @see <a href=\"https://github.com/RinOrz/LSPosed/blob/a44e1f1cdf0c5e5ebfaface828e5907f5425df1b/benchmark/src/result/ReflectionCacheBenchmark.json\">benchmarks for ART</a>\n     * @see <a href=\"https://github.com/meowool-catnip/cloak/blob/main/api/src/benchmark/kotlin/com/meowool/cloak/ReflectionObjectAccessTests.kt#L37-L65\">benchmarks for JVM</a>\n     */\n    private abstract static class MemberCacheKey {\n        private final int hash;\n\n        protected MemberCacheKey(int hash) {\n            this.hash = hash;\n        }\n\n        @Override\n        public abstract boolean equals(@Nullable Object obj);\n\n        @Override\n        public final int hashCode() {\n            return hash;\n        }\n\n        static final class Constructor extends MemberCacheKey {\n            private final Class<?> clazz;\n            private final Class<?>[] parameters;\n            private final boolean isExact;\n\n            public Constructor(Class<?> clazz, Class<?>[] parameters, boolean isExact) {\n                super(31 * Objects.hash(clazz, isExact) + Arrays.hashCode(parameters));\n                this.clazz = clazz;\n                this.parameters = parameters;\n                this.isExact = isExact;\n            }\n\n            @Override\n            public boolean equals(Object o) {\n                if (this == o) return true;\n                if (!(o instanceof Constructor)) return false;\n                Constructor that = (Constructor) o;\n                return isExact == that.isExact && Objects.equals(clazz, that.clazz) && Arrays.equals(parameters, that.parameters);\n            }\n\n            @NonNull\n            @Override\n            public String toString() {\n                var str = clazz.getName() + getParametersString(parameters);\n                if (isExact) {\n                    return str + \"#exact\";\n                } else {\n                    return str;\n                }\n            }\n        }\n\n        static final class Field extends MemberCacheKey {\n            private final Class<?> clazz;\n            private final String name;\n\n            public Field(Class<?> clazz, String name) {\n                super(Objects.hash(clazz, name));\n                this.clazz = clazz;\n                this.name = name;\n            }\n\n            @Override\n            public boolean equals(Object o) {\n                if (this == o) return true;\n                if (!(o instanceof Field)) return false;\n                Field field = (Field) o;\n                return Objects.equals(clazz, field.clazz) && Objects.equals(name, field.name);\n            }\n\n            @NonNull\n            @Override\n            public String toString() {\n                return clazz.getName() + \"#\" + name;\n            }\n        }\n\n        static final class Method extends MemberCacheKey {\n            private final Class<?> clazz;\n            private final String name;\n            private final Class<?>[] parameters;\n            private final boolean isExact;\n\n            public Method(Class<?> clazz, String name, Class<?>[] parameters, boolean isExact) {\n                super(31 * Objects.hash(clazz, name, isExact) + Arrays.hashCode(parameters));\n                this.clazz = clazz;\n                this.name = name;\n                this.parameters = parameters;\n                this.isExact = isExact;\n            }\n\n            @Override\n            public boolean equals(Object o) {\n                if (this == o) return true;\n                if (!(o instanceof Method)) return false;\n                Method method = (Method) o;\n                return isExact == method.isExact && Objects.equals(clazz, method.clazz) && Objects.equals(name, method.name) && Arrays.equals(parameters, method.parameters);\n            }\n\n            @NonNull\n            @Override\n            public String toString() {\n                var str = clazz.getName() + '#' + name + getParametersString(parameters);\n                if (isExact) {\n                    return str + \"#exact\";\n                } else {\n                    return str;\n                }\n            }\n        }\n    }\n\n    /**\n     * Look up a class with the specified class loader.\n     *\n     * <p>There are various allowed syntaxes for the class name, but it's recommended to use one of\n     * these:\n     * <ul>\n     *   <li>{@code java.lang.String}\n     *   <li>{@code java.lang.String[]} (array)\n     *   <li>{@code android.app.ActivityThread.ResourcesKey}\n     *   <li>{@code android.app.ActivityThread$ResourcesKey}\n     * </ul>\n     *\n     * @param className   The class name in one of the formats mentioned above.\n     * @param classLoader The class loader, or {@code null} for the boot class loader.\n     * @return A reference to the class.\n     * @throws ClassNotFoundError In case the class was not found.\n     */\n    public static Class<?> findClass(String className, ClassLoader classLoader) {\n        if (classLoader == null)\n            classLoader = XposedBridge.BOOTCLASSLOADER;\n        try {\n            return ClassUtilsX.getClass(classLoader, className, false);\n        } catch (ClassNotFoundException e) {\n            throw new ClassNotFoundError(e);\n        }\n    }\n\n    /**\n     * Look up and return a class if it exists.\n     * Like {@link #findClass}, but doesn't throw an exception if the class doesn't exist.\n     *\n     * @param className   The class name.\n     * @param classLoader The class loader, or {@code null} for the boot class loader.\n     * @return A reference to the class, or {@code null} if it doesn't exist.\n     */\n    public static Class<?> findClassIfExists(String className, ClassLoader classLoader) {\n        try {\n            return findClass(className, classLoader);\n        } catch (ClassNotFoundError e) {\n            return null;\n        }\n    }\n\n    /**\n     * Look up a field in a class and set it to accessible.\n     *\n     * @param clazz     The class which either declares or inherits the field.\n     * @param fieldName The field name.\n     * @return A reference to the field.\n     * @throws NoSuchFieldError In case the field was not found.\n     */\n    public static Field findField(Class<?> clazz, String fieldName) {\n        var key = new MemberCacheKey.Field(clazz, fieldName);\n\n        return fieldCache.computeIfAbsent(key, k -> {\n            try {\n                Field newField = findFieldRecursiveImpl(k.clazz, k.name);\n                newField.setAccessible(true);\n                return Optional.of(newField);\n            } catch (NoSuchFieldException e) {\n                return Optional.empty();\n            }\n        }).orElseThrow(() -> new NoSuchFieldError(key.toString()));\n    }\n\n    /**\n     * Look up and return a field if it exists.\n     * Like {@link #findField}, but doesn't throw an exception if the field doesn't exist.\n     *\n     * @param clazz     The class which either declares or inherits the field.\n     * @param fieldName The field name.\n     * @return A reference to the field, or {@code null} if it doesn't exist.\n     */\n    public static Field findFieldIfExists(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName);\n        } catch (NoSuchFieldError e) {\n            return null;\n        }\n    }\n\n    private static Field findFieldRecursiveImpl(Class<?> clazz, String fieldName) throws NoSuchFieldException {\n        try {\n            return clazz.getDeclaredField(fieldName);\n        } catch (NoSuchFieldException e) {\n            while (true) {\n                clazz = clazz.getSuperclass();\n                if (clazz == null || clazz.equals(Object.class))\n                    break;\n\n                try {\n                    return clazz.getDeclaredField(fieldName);\n                } catch (NoSuchFieldException ignored) {\n                }\n            }\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the first field of the given type in a class.\n     * Might be useful for Proguard'ed classes to identify fields with unique types.\n     *\n     * @param clazz The class which either declares or inherits the field.\n     * @param type  The type of the field.\n     * @return A reference to the first field of the given type.\n     * @throws NoSuchFieldError In case no matching field was not found.\n     */\n    public static Field findFirstFieldByExactType(Class<?> clazz, Class<?> type) {\n        Class<?> clz = clazz;\n        do {\n            for (Field field : clz.getDeclaredFields()) {\n                if (field.getType() == type) {\n                    field.setAccessible(true);\n                    return field;\n                }\n            }\n        } while ((clz = clz.getSuperclass()) != null);\n\n        throw new NoSuchFieldError(\"Field of type \" + type.getName() + \" in class \" + clazz.getName());\n    }\n\n    /**\n     * Look up a method and hook it. See {@link #findAndHookMethod(String, ClassLoader, String, Object...)}\n     * for details.\n     */\n    public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {\n        if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length - 1] instanceof XC_MethodHook))\n            throw new IllegalArgumentException(\"no callback defined\");\n\n        XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length - 1];\n        Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));\n\n        return XposedBridge.hookMethod(m, callback);\n    }\n\n    /**\n     * Look up a method and hook it. The last argument must be the callback for the hook.\n     *\n     * <p>This combines calls to {@link #findMethodExact(Class, String, Object...)} and\n     * {@link XposedBridge#hookMethod}.\n     *\n     * <p class=\"warning\">The method must be declared or overridden in the given class, inherited\n     * methods are not considered! That's because each method implementation exists only once in\n     * the memory, and when classes inherit it, they just get another reference to the implementation.\n     * Hooking a method therefore applies to all classes inheriting the same implementation. You\n     * have to expect that the hook applies to subclasses (unless they override the method), but you\n     * shouldn't have to worry about hooks applying to superclasses, hence this \"limitation\".\n     * There could be undesired or even dangerous hooks otherwise, e.g. if you hook\n     * {@code SomeClass.equals()} and that class doesn't override the {@code equals()} on some ROMs,\n     * making you hook {@code Object.equals()} instead.\n     *\n     * <p>There are two ways to specify the parameter types. If you already have a reference to the\n     * {@link Class}, use that. For Android framework classes, you can often use something like\n     * {@code String.class}. If you don't have the class reference, you can simply use the\n     * full class name as a string, e.g. {@code java.lang.String} or {@code com.example.MyClass}.\n     * It will be passed to {@link #findClass} with the same class loader that is used for the target\n     * method, see its documentation for the allowed notations.\n     *\n     * <p>Primitive types, such as {@code int}, can be specified using {@code int.class} (recommended)\n     * or {@code Integer.TYPE}. Note that {@code Integer.class} doesn't refer to {@code int} but to\n     * {@code Integer}, which is a normal class (boxed primitive). Therefore it must not be used when\n     * the method expects an {@code int} parameter - it has to be used for {@code Integer} parameters\n     * though, so check the method signature in detail.\n     *\n     * <p>As last argument to this method (after the list of target method parameters), you need\n     * to specify the callback that should be executed when the method is invoked. It's usually\n     * an anonymous subclass of {@link XC_MethodHook} or {@link XC_MethodReplacement}.\n     *\n     * <p><b>Example</b>\n     * <pre class=\"prettyprint\">\n     * // In order to hook this method ...\n     * package com.example;\n     * public class SomeClass {\n     *   public int doSomething(String s, int i, MyClass m) {\n     *     ...\n     *   }\n     * }\n     *\n     * // ... you can use this call:\n     * findAndHookMethod(\"com.example.SomeClass\", lpparam.classLoader, String.class, int.class, \"com.example.MyClass\", new XC_MethodHook() {\n     *   &#64;Override\n     *   protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n     *     String oldText = (String) param.args[0];\n     *     Log.d(\"MyModule\", oldText);\n     *\n     *     param.args[0] = \"test\";\n     *     param.args[1] = 42; // auto-boxing is working here\n     *     setBooleanField(param.args[2], \"great\", true);\n     *\n     *     // This would not work (as MyClass can't be resolved at compile time):\n     *     //   MyClass myClass = (MyClass) param.args[2];\n     *     //   myClass.great = true;\n     *   }\n     * });\n     * </pre>\n     *\n     * @param className                 The name of the class which implements the method.\n     * @param classLoader               The class loader for resolving the target and parameter classes.\n     * @param methodName                The target method name.\n     * @param parameterTypesAndCallback The parameter types of the target method, plus the callback.\n     * @return An object which can be used to remove the callback again.\n     * @throws NoSuchMethodError  In case the method was not found.\n     * @throws ClassNotFoundError In case the target class or one of the parameter types couldn't be resolved.\n     */\n    public static XC_MethodHook.Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) {\n        return findAndHookMethod(findClass(className, classLoader), methodName, parameterTypesAndCallback);\n    }\n\n    /**\n     * Look up a method in a class and set it to accessible.\n     * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Method findMethodExact(Class<?> clazz, String methodName, Object... parameterTypes) {\n        return findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypes));\n    }\n\n    /**\n     * Look up and return a method if it exists.\n     * See {@link #findMethodExactIfExists(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Method findMethodExactIfExists(Class<?> clazz, String methodName, Object... parameterTypes) {\n        try {\n            return findMethodExact(clazz, methodName, parameterTypes);\n        } catch (ClassNotFoundError | NoSuchMethodError e) {\n            return null;\n        }\n    }\n\n    /**\n     * Look up a method in a class and set it to accessible.\n     * The method must be declared or overridden in the given class.\n     *\n     * <p>See {@link #findAndHookMethod(String, ClassLoader, String, Object...)} for details about\n     * the method and parameter type resolution.\n     *\n     * @param className      The name of the class which implements the method.\n     * @param classLoader    The class loader for resolving the target and parameter classes.\n     * @param methodName     The target method name.\n     * @param parameterTypes The parameter types of the target method.\n     * @return A reference to the method.\n     * @throws NoSuchMethodError  In case the method was not found.\n     * @throws ClassNotFoundError In case the target class or one of the parameter types couldn't be resolved.\n     */\n    public static Method findMethodExact(String className, ClassLoader classLoader, String methodName, Object... parameterTypes) {\n        return findMethodExact(findClass(className, classLoader), methodName, getParameterClasses(classLoader, parameterTypes));\n    }\n\n    /**\n     * Look up and return a method if it exists.\n     * Like {@link #findMethodExact(String, ClassLoader, String, Object...)}, but doesn't throw an\n     * exception if the method doesn't exist.\n     *\n     * @param className      The name of the class which implements the method.\n     * @param classLoader    The class loader for resolving the target and parameter classes.\n     * @param methodName     The target method name.\n     * @param parameterTypes The parameter types of the target method.\n     * @return A reference to the method, or {@code null} if it doesn't exist.\n     */\n    public static Method findMethodExactIfExists(String className, ClassLoader classLoader, String methodName, Object... parameterTypes) {\n        try {\n            return findMethodExact(className, classLoader, methodName, parameterTypes);\n        } catch (ClassNotFoundError | NoSuchMethodError e) {\n            return null;\n        }\n    }\n\n    /**\n     * Look up a method in a class and set it to accessible.\n     * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details.\n     *\n     * <p>This variant requires that you already have reference to all the parameter types.\n     */\n    public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {\n        var key = new MemberCacheKey.Method(clazz, methodName, parameterTypes, true);\n\n        return methodCache.computeIfAbsent(key, k -> {\n            try {\n                Method method = k.clazz.getDeclaredMethod(k.name, k.parameters);\n                method.setAccessible(true);\n                return Optional.of(method);\n            } catch (NoSuchMethodException e) {\n                return Optional.empty();\n            }\n        }).orElseThrow(() -> new NoSuchMethodError(key.toString()));\n    }\n\n    /**\n     * Returns an array of all methods declared/overridden in a class with the specified parameter types.\n     *\n     * <p>The return type is optional, it will not be compared if it is {@code null}.\n     * Use {@code void.class} if you want to search for methods returning nothing.\n     *\n     * @param clazz          The class to look in.\n     * @param returnType     The return type, or {@code null} (see above).\n     * @param parameterTypes The parameter types.\n     * @return An array with matching methods, all set to accessible already.\n     */\n    public static Method[] findMethodsByExactParameters(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes) {\n        List<Method> result = new LinkedList<>();\n        for (Method method : clazz.getDeclaredMethods()) {\n            if (returnType != null && returnType != method.getReturnType())\n                continue;\n\n            Class<?>[] methodParameterTypes = method.getParameterTypes();\n            if (parameterTypes.length != methodParameterTypes.length)\n                continue;\n\n            boolean match = true;\n            for (int i = 0; i < parameterTypes.length; i++) {\n                if (parameterTypes[i] != methodParameterTypes[i]) {\n                    match = false;\n                    break;\n                }\n            }\n\n            if (!match)\n                continue;\n\n            method.setAccessible(true);\n            result.add(method);\n        }\n        return result.toArray(new Method[result.size()]);\n    }\n\n    /**\n     * Look up a method in a class and set it to accessible.\n     *\n     * <p>This does'nt only look for exact matches, but for the best match. All considered candidates\n     * must be compatible with the given parameter types, i.e. the parameters must be assignable\n     * to the method's formal parameters. Inherited methods are considered here.\n     *\n     * @param clazz          The class which declares, inherits or overrides the method.\n     * @param methodName     The method name.\n     * @param parameterTypes The types of the method's parameters.\n     * @return A reference to the best-matching method.\n     * @throws NoSuchMethodError In case no suitable method was found.\n     */\n    public static Method findMethodBestMatch(Class<?> clazz, String methodName, Class<?>... parameterTypes) {\n        // find the exact matching method first\n        try {\n            return findMethodExact(clazz, methodName, parameterTypes);\n        } catch (NoSuchMethodError ignored) {\n        }\n\n        // then find the best match\n        var key = new MemberCacheKey.Method(clazz, methodName, parameterTypes, false);\n\n        return methodCache.computeIfAbsent(key, k -> {\n            Method bestMatch = null;\n            Class<?> clz = k.clazz;\n            boolean considerPrivateMethods = true;\n            do {\n                for (Method method : clz.getDeclaredMethods()) {\n                    // don't consider private methods of superclasses\n                    if (!considerPrivateMethods && Modifier.isPrivate(method.getModifiers()))\n                        continue;\n\n                    // compare name and parameters\n                    if (method.getName().equals(k.name) && ClassUtilsX.isAssignable(\n                            k.parameters,\n                            method.getParameterTypes(),\n                            true)) {\n                        // get accessible version of method\n                        if (bestMatch == null || MemberUtilsX.compareMethodFit(\n                                method,\n                                bestMatch,\n                                k.parameters) < 0) {\n                            bestMatch = method;\n                        }\n                    }\n                }\n                considerPrivateMethods = false;\n            } while ((clz = clz.getSuperclass()) != null);\n\n            if (bestMatch != null) {\n                bestMatch.setAccessible(true);\n                return Optional.of(bestMatch);\n            } else {\n                return Optional.empty();\n            }\n        }).orElseThrow(() -> new NoSuchMethodError(key.toString()));\n    }\n\n    /**\n     * Look up a method in a class and set it to accessible.\n     *\n     * <p>See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant\n     * determines the parameter types from the classes of the given objects.\n     */\n    public static Method findMethodBestMatch(Class<?> clazz, String methodName, Object... args) {\n        return findMethodBestMatch(clazz, methodName, getParameterTypes(args));\n    }\n\n    /**\n     * Look up a method in a class and set it to accessible.\n     *\n     * <p>See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant\n     * determines the parameter types from the classes of the given objects. For any item that is\n     * {@code null}, the type is taken from {@code parameterTypes} instead.\n     */\n    public static Method findMethodBestMatch(Class<?> clazz, String methodName, Class<?>[] parameterTypes, Object[] args) {\n        Class<?>[] argsClasses = null;\n        for (int i = 0; i < parameterTypes.length; i++) {\n            if (parameterTypes[i] != null)\n                continue;\n            if (argsClasses == null)\n                argsClasses = getParameterTypes(args);\n            parameterTypes[i] = argsClasses[i];\n        }\n        return findMethodBestMatch(clazz, methodName, parameterTypes);\n    }\n\n    /**\n     * Returns an array with the classes of the given objects.\n     */\n    public static Class<?>[] getParameterTypes(Object... args) {\n        Class<?>[] clazzes = new Class<?>[args.length];\n        for (int i = 0; i < args.length; i++) {\n            clazzes[i] = (args[i] != null) ? args[i].getClass() : null;\n        }\n        return clazzes;\n    }\n\n    /**\n     * Retrieve classes from an array, where each element might either be a Class\n     * already, or a String with the full class name.\n     */\n    private static Class<?>[] getParameterClasses(ClassLoader classLoader, Object[] parameterTypesAndCallback) {\n        Class<?>[] parameterClasses = null;\n        for (int i = parameterTypesAndCallback.length - 1; i >= 0; i--) {\n            Object type = parameterTypesAndCallback[i];\n            if (type == null)\n                throw new ClassNotFoundError(\"parameter type must not be null\", null);\n\n            // ignore trailing callback\n            if (type instanceof XC_MethodHook)\n                continue;\n\n            if (parameterClasses == null)\n                parameterClasses = new Class<?>[i + 1];\n\n            if (type instanceof Class)\n                parameterClasses[i] = (Class<?>) type;\n            else if (type instanceof String)\n                parameterClasses[i] = findClass((String) type, classLoader);\n            else\n                throw new ClassNotFoundError(\"parameter type must either be specified as Class or String\", null);\n        }\n\n        // if there are no arguments for the method\n        if (parameterClasses == null)\n            parameterClasses = new Class<?>[0];\n\n        return parameterClasses;\n    }\n\n    /**\n     * Returns an array of the given classes.\n     */\n    public static Class<?>[] getClassesAsArray(Class<?>... clazzes) {\n        return clazzes;\n    }\n\n    private static String getParametersString(Class<?>... clazzes) {\n        StringBuilder sb = new StringBuilder(\"(\");\n        boolean first = true;\n        for (Class<?> clazz : clazzes) {\n            if (first)\n                first = false;\n            else\n                sb.append(\",\");\n\n            if (clazz != null)\n                sb.append(clazz.getCanonicalName());\n            else\n                sb.append(\"null\");\n        }\n        sb.append(\")\");\n        return sb.toString();\n    }\n\n    /**\n     * Look up a constructor of a class and set it to accessible.\n     * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Constructor<?> findConstructorExact(Class<?> clazz, Object... parameterTypes) {\n        return findConstructorExact(clazz, getParameterClasses(clazz.getClassLoader(), parameterTypes));\n    }\n\n    /**\n     * Look up and return a constructor if it exists.\n     * See {@link #findMethodExactIfExists(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Constructor<?> findConstructorExactIfExists(Class<?> clazz, Object... parameterTypes) {\n        try {\n            return findConstructorExact(clazz, parameterTypes);\n        } catch (ClassNotFoundError | NoSuchMethodError e) {\n            return null;\n        }\n    }\n\n    /**\n     * Look up a constructor of a class and set it to accessible.\n     * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Constructor<?> findConstructorExact(String className, ClassLoader classLoader, Object... parameterTypes) {\n        return findConstructorExact(findClass(className, classLoader), getParameterClasses(classLoader, parameterTypes));\n    }\n\n    /**\n     * Look up and return a constructor if it exists.\n     * See {@link #findMethodExactIfExists(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Constructor<?> findConstructorExactIfExists(String className, ClassLoader classLoader, Object... parameterTypes) {\n        try {\n            return findConstructorExact(className, classLoader, parameterTypes);\n        } catch (ClassNotFoundError | NoSuchMethodError e) {\n            return null;\n        }\n    }\n\n    /**\n     * Look up a constructor of a class and set it to accessible.\n     * See {@link #findMethodExact(String, ClassLoader, String, Object...)} for details.\n     */\n    public static Constructor<?> findConstructorExact(Class<?> clazz, Class<?>... parameterTypes) {\n        var key = new MemberCacheKey.Constructor(clazz, parameterTypes, true);\n\n        return constructorCache.computeIfAbsent(key, k -> {\n            try {\n                Constructor<?> constructor = k.clazz.getDeclaredConstructor(k.parameters);\n                constructor.setAccessible(true);\n                return Optional.of(constructor);\n            } catch (NoSuchMethodException e) {\n                return Optional.empty();\n            }\n        }).orElseThrow(() -> new NoSuchMethodError(key.toString()));\n    }\n\n    /**\n     * Look up a constructor and hook it. See {@link #findAndHookMethod(String, ClassLoader, String, Object...)}\n     * for details.\n     */\n    public static XC_MethodHook.Unhook findAndHookConstructor(Class<?> clazz, Object... parameterTypesAndCallback) {\n        if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length - 1] instanceof XC_MethodHook))\n            throw new IllegalArgumentException(\"no callback defined\");\n\n        XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length - 1];\n        Constructor<?> m = findConstructorExact(clazz, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));\n\n        return XposedBridge.hookMethod(m, callback);\n    }\n\n    /**\n     * Look up a constructor and hook it. See {@link #findAndHookMethod(String, ClassLoader, String, Object...)}\n     * for details.\n     */\n    public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) {\n        return findAndHookConstructor(findClass(className, classLoader), parameterTypesAndCallback);\n    }\n\n    /**\n     * Look up a constructor in a class and set it to accessible.\n     *\n     * <p>See {@link #findMethodBestMatch(Class, String, Class...)} for details.\n     */\n    public static Constructor<?> findConstructorBestMatch(Class<?> clazz, Class<?>... parameterTypes) {\n        // find the exact matching constructor first\n        try {\n            return findConstructorExact(clazz, parameterTypes);\n        } catch (NoSuchMethodError ignored) {\n        }\n\n        // then find the best match\n        var key = new MemberCacheKey.Constructor(clazz, parameterTypes, false);\n\n        return constructorCache.computeIfAbsent(key, k -> {\n            Constructor<?> bestMatch = null;\n            Constructor<?>[] constructors = k.clazz.getDeclaredConstructors();\n            for (Constructor<?> constructor : constructors) {\n                // compare name and parameters\n                if (ClassUtilsX.isAssignable(\n                        k.parameters,\n                        constructor.getParameterTypes(),\n                        true)) {\n                    // get accessible version of method\n                    if (bestMatch == null || MemberUtilsX.compareConstructorFit(\n                            constructor,\n                            bestMatch,\n                            k.parameters) < 0) {\n                        bestMatch = constructor;\n                    }\n                }\n            }\n\n            if (bestMatch != null) {\n                bestMatch.setAccessible(true);\n                return Optional.of(bestMatch);\n            } else {\n                return Optional.empty();\n            }\n        }).orElseThrow(() -> new NoSuchMethodError(key.toString()));\n    }\n\n    /**\n     * Look up a constructor in a class and set it to accessible.\n     *\n     * <p>See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant\n     * determines the parameter types from the classes of the given objects.\n     */\n    public static Constructor<?> findConstructorBestMatch(Class<?> clazz, Object... args) {\n        return findConstructorBestMatch(clazz, getParameterTypes(args));\n    }\n\n    /**\n     * Look up a constructor in a class and set it to accessible.\n     *\n     * <p>See {@link #findMethodBestMatch(Class, String, Class...)} for details. This variant\n     * determines the parameter types from the classes of the given objects. For any item that is\n     * {@code null}, the type is taken from {@code parameterTypes} instead.\n     */\n    public static Constructor<?> findConstructorBestMatch(Class<?> clazz, Class<?>[] parameterTypes, Object[] args) {\n        Class<?>[] argsClasses = null;\n        for (int i = 0; i < parameterTypes.length; i++) {\n            if (parameterTypes[i] != null)\n                continue;\n            if (argsClasses == null)\n                argsClasses = getParameterTypes(args);\n            parameterTypes[i] = argsClasses[i];\n        }\n        return findConstructorBestMatch(clazz, parameterTypes);\n    }\n\n    /**\n     * Thrown when a class loader is unable to find a class. Unlike {@link ClassNotFoundException},\n     * callers are not forced to explicitly catch this. If uncaught, the error will be passed to the\n     * next caller in the stack.\n     */\n    public static final class ClassNotFoundError extends Error {\n        private static final long serialVersionUID = -1070936889459514628L;\n\n        /**\n         * @hide\n         */\n        public ClassNotFoundError(Throwable cause) {\n            super(cause);\n        }\n\n        /**\n         * @hide\n         */\n        public ClassNotFoundError(String detailMessage, Throwable cause) {\n            super(detailMessage, cause);\n        }\n    }\n\n    /**\n     * Returns the index of the first parameter declared with the given type.\n     *\n     * @throws NoSuchFieldError if there is no parameter with that type.\n     * @hide\n     */\n    public static int getFirstParameterIndexByType(Member method, Class<?> type) {\n        Class<?>[] classes = (method instanceof Method) ?\n                ((Method) method).getParameterTypes() : ((Constructor) method).getParameterTypes();\n        for (int i = 0; i < classes.length; i++) {\n            if (classes[i] == type) {\n                return i;\n            }\n        }\n        throw new NoSuchFieldError(\"No parameter of type \" + type + \" found in \" + method);\n    }\n\n    /**\n     * Returns the index of the parameter declared with the given type, ensuring that there is exactly one such parameter.\n     *\n     * @throws NoSuchFieldError if there is no or more than one parameter with that type.\n     * @hide\n     */\n    public static int getParameterIndexByType(Member method, Class<?> type) {\n        Class<?>[] classes = (method instanceof Method) ?\n                ((Method) method).getParameterTypes() : ((Constructor) method).getParameterTypes();\n        int idx = -1;\n        for (int i = 0; i < classes.length; i++) {\n            if (classes[i] == type) {\n                if (idx == -1) {\n                    idx = i;\n                } else {\n                    throw new NoSuchFieldError(\"More than one parameter of type \" + type + \" found in \" + method);\n                }\n            }\n        }\n        if (idx != -1) {\n            return idx;\n        } else {\n            throw new NoSuchFieldError(\"No parameter of type \" + type + \" found in \" + method);\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Sets the value of an object field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setObjectField(Object obj, String fieldName, Object value) {\n        try {\n            findField(obj.getClass(), fieldName).set(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code boolean} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setBooleanField(Object obj, String fieldName, boolean value) {\n        try {\n            findField(obj.getClass(), fieldName).setBoolean(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code byte} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setByteField(Object obj, String fieldName, byte value) {\n        try {\n            findField(obj.getClass(), fieldName).setByte(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code char} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setCharField(Object obj, String fieldName, char value) {\n        try {\n            findField(obj.getClass(), fieldName).setChar(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code double} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setDoubleField(Object obj, String fieldName, double value) {\n        try {\n            findField(obj.getClass(), fieldName).setDouble(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code float} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setFloatField(Object obj, String fieldName, float value) {\n        try {\n            findField(obj.getClass(), fieldName).setFloat(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of an {@code int} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setIntField(Object obj, String fieldName, int value) {\n        try {\n            findField(obj.getClass(), fieldName).setInt(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code long} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setLongField(Object obj, String fieldName, long value) {\n        try {\n            findField(obj.getClass(), fieldName).setLong(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a {@code short} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static void setShortField(Object obj, String fieldName, short value) {\n        try {\n            findField(obj.getClass(), fieldName).setShort(obj, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Returns the value of an object field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static Object getObjectField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).get(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * For inner classes, returns the surrounding instance, i.e. the {@code this} reference of the surrounding class.\n     */\n    public static Object getSurroundingThis(Object obj) {\n        return getObjectField(obj, \"this$0\");\n    }\n\n    /**\n     * Returns the value of a {@code boolean} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    public static boolean getBooleanField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getBoolean(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a {@code byte} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static byte getByteField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getByte(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a {@code char} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static char getCharField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getChar(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a {@code double} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static double getDoubleField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getDouble(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a {@code float} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static float getFloatField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getFloat(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of an {@code int} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static int getIntField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getInt(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a {@code long} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static long getLongField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getLong(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a {@code short} field in the given object instance. A class reference is not sufficient! See also {@link #findField}.\n     */\n    public static short getShortField(Object obj, String fieldName) {\n        try {\n            return findField(obj.getClass(), fieldName).getShort(obj);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Sets the value of a static object field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticObjectField(Class<?> clazz, String fieldName, Object value) {\n        try {\n            findField(clazz, fieldName).set(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code boolean} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticBooleanField(Class<?> clazz, String fieldName, boolean value) {\n        try {\n            findField(clazz, fieldName).setBoolean(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code byte} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticByteField(Class<?> clazz, String fieldName, byte value) {\n        try {\n            findField(clazz, fieldName).setByte(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code char} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticCharField(Class<?> clazz, String fieldName, char value) {\n        try {\n            findField(clazz, fieldName).setChar(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code double} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticDoubleField(Class<?> clazz, String fieldName, double value) {\n        try {\n            findField(clazz, fieldName).setDouble(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code float} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticFloatField(Class<?> clazz, String fieldName, float value) {\n        try {\n            findField(clazz, fieldName).setFloat(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code int} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticIntField(Class<?> clazz, String fieldName, int value) {\n        try {\n            findField(clazz, fieldName).setInt(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code long} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticLongField(Class<?> clazz, String fieldName, long value) {\n        try {\n            findField(clazz, fieldName).setLong(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code short} field in the given class. See also {@link #findField}.\n     */\n    public static void setStaticShortField(Class<?> clazz, String fieldName, short value) {\n        try {\n            findField(clazz, fieldName).setShort(null, value);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Returns the value of a static object field in the given class. See also {@link #findField}.\n     */\n    public static Object getStaticObjectField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).get(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Returns the value of a static {@code boolean} field in the given class. See also {@link #findField}.\n     */\n    public static boolean getStaticBooleanField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getBoolean(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code byte} field in the given class. See also {@link #findField}.\n     */\n    public static byte getStaticByteField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getByte(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code char} field in the given class. See also {@link #findField}.\n     */\n    public static char getStaticCharField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getChar(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code double} field in the given class. See also {@link #findField}.\n     */\n    public static double getStaticDoubleField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getDouble(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code float} field in the given class. See also {@link #findField}.\n     */\n    public static float getStaticFloatField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getFloat(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code int} field in the given class. See also {@link #findField}.\n     */\n    public static int getStaticIntField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getInt(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code long} field in the given class. See also {@link #findField}.\n     */\n    public static long getStaticLongField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getLong(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    /**\n     * Sets the value of a static {@code short} field in the given class. See also {@link #findField}.\n     */\n    public static short getStaticShortField(Class<?> clazz, String fieldName) {\n        try {\n            return findField(clazz, fieldName).getShort(null);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Calls an instance or static method of the given object.\n     * The method is resolved using {@link #findMethodBestMatch(Class, String, Object...)}.\n     *\n     * @param obj        The object instance. A class reference is not sufficient!\n     * @param methodName The method name.\n     * @param args       The arguments for the method call.\n     * @throws NoSuchMethodError     In case no suitable method was found.\n     * @throws InvocationTargetError In case an exception was thrown by the invoked method.\n     */\n    public static Object callMethod(Object obj, String methodName, Object... args) {\n        try {\n            return findMethodBestMatch(obj.getClass(), methodName, args).invoke(obj, args);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        } catch (InvocationTargetException e) {\n            throw new InvocationTargetError(e.getCause());\n        }\n    }\n\n    /**\n     * Calls an instance or static method of the given object.\n     * See {@link #callMethod(Object, String, Object...)}.\n     *\n     * <p>This variant allows you to specify parameter types, which can help in case there are multiple\n     * methods with the same name, especially if you call it with {@code null} parameters.\n     */\n    public static Object callMethod(Object obj, String methodName, Class<?>[] parameterTypes, Object... args) {\n        try {\n            return findMethodBestMatch(obj.getClass(), methodName, parameterTypes, args).invoke(obj, args);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        } catch (InvocationTargetException e) {\n            throw new InvocationTargetError(e.getCause());\n        }\n    }\n\n    /**\n     * Calls a static method of the given class.\n     * The method is resolved using {@link #findMethodBestMatch(Class, String, Object...)}.\n     *\n     * @param clazz      The class reference.\n     * @param methodName The method name.\n     * @param args       The arguments for the method call.\n     * @throws NoSuchMethodError     In case no suitable method was found.\n     * @throws InvocationTargetError In case an exception was thrown by the invoked method.\n     */\n    public static Object callStaticMethod(Class<?> clazz, String methodName, Object... args) {\n        try {\n            return findMethodBestMatch(clazz, methodName, args).invoke(null, args);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        } catch (InvocationTargetException e) {\n            throw new InvocationTargetError(e.getCause());\n        }\n    }\n\n    /**\n     * Calls a static method of the given class.\n     * See {@link #callStaticMethod(Class, String, Object...)}.\n     *\n     * <p>This variant allows you to specify parameter types, which can help in case there are multiple\n     * methods with the same name, especially if you call it with {@code null} parameters.\n     */\n    public static Object callStaticMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes, Object... args) {\n        try {\n            return findMethodBestMatch(clazz, methodName, parameterTypes, args).invoke(null, args);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        } catch (InvocationTargetException e) {\n            throw new InvocationTargetError(e.getCause());\n        }\n    }\n\n    /**\n     * This class provides a wrapper for an exception thrown by a method invocation.\n     *\n     * @see #callMethod(Object, String, Object...)\n     * @see #callStaticMethod(Class, String, Object...)\n     * @see #newInstance(Class, Object...)\n     */\n    public static final class InvocationTargetError extends Error {\n        private static final long serialVersionUID = -1070936889459514628L;\n\n        /**\n         * @hide\n         */\n        public InvocationTargetError(Throwable cause) {\n            super(cause);\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Creates a new instance of the given class.\n     * The constructor is resolved using {@link #findConstructorBestMatch(Class, Object...)}.\n     *\n     * @param clazz The class reference.\n     * @param args  The arguments for the constructor call.\n     * @throws NoSuchMethodError     In case no suitable constructor was found.\n     * @throws InvocationTargetError In case an exception was thrown by the invoked method.\n     * @throws InstantiationError    In case the class cannot be instantiated.\n     */\n    public static Object newInstance(Class<?> clazz, Object... args) {\n        try {\n            return findConstructorBestMatch(clazz, args).newInstance(args);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        } catch (InvocationTargetException e) {\n            throw new InvocationTargetError(e.getCause());\n        } catch (InstantiationException e) {\n            throw new InstantiationError(e.getMessage());\n        }\n    }\n\n    /**\n     * Creates a new instance of the given class.\n     * See {@link #newInstance(Class, Object...)}.\n     *\n     * <p>This variant allows you to specify parameter types, which can help in case there are multiple\n     * constructors with the same name, especially if you call it with {@code null} parameters.\n     */\n    public static Object newInstance(Class<?> clazz, Class<?>[] parameterTypes, Object... args) {\n        try {\n            return findConstructorBestMatch(clazz, parameterTypes, args).newInstance(args);\n        } catch (IllegalAccessException e) {\n            // should not happen\n            XposedBridge.log(e);\n            throw new IllegalAccessError(e.getMessage());\n        } catch (IllegalArgumentException e) {\n            throw e;\n        } catch (InvocationTargetException e) {\n            throw new InvocationTargetError(e.getCause());\n        } catch (InstantiationException e) {\n            throw new InstantiationError(e.getMessage());\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Attaches any value to an object instance. This simulates adding an instance field.\n     * The value can be retrieved again with {@link #getAdditionalInstanceField}.\n     *\n     * @param obj   The object instance for which the value should be stored.\n     * @param key   The key in the value map for this object instance.\n     * @param value The value to store.\n     * @return The previously stored value for this instance/key combination, or {@code null} if there was none.\n     */\n    public static Object setAdditionalInstanceField(Object obj, String key, Object value) {\n        if (obj == null)\n            throw new NullPointerException(\"object must not be null\");\n        if (key == null)\n            throw new NullPointerException(\"key must not be null\");\n\n        HashMap<String, Object> objectFields;\n        synchronized (additionalFields) {\n            objectFields = additionalFields.get(obj);\n            if (objectFields == null) {\n                objectFields = new HashMap<>();\n                additionalFields.put(obj, objectFields);\n            }\n        }\n\n        synchronized (objectFields) {\n            return objectFields.put(key, value);\n        }\n    }\n\n    /**\n     * Returns a value which was stored with {@link #setAdditionalInstanceField}.\n     *\n     * @param obj The object instance for which the value has been stored.\n     * @param key The key in the value map for this object instance.\n     * @return The stored value for this instance/key combination, or {@code null} if there is none.\n     */\n    public static Object getAdditionalInstanceField(Object obj, String key) {\n        if (obj == null)\n            throw new NullPointerException(\"object must not be null\");\n        if (key == null)\n            throw new NullPointerException(\"key must not be null\");\n\n        HashMap<String, Object> objectFields;\n        synchronized (additionalFields) {\n            objectFields = additionalFields.get(obj);\n            if (objectFields == null)\n                return null;\n        }\n\n        synchronized (objectFields) {\n            return objectFields.get(key);\n        }\n    }\n\n    /**\n     * Removes and returns a value which was stored with {@link #setAdditionalInstanceField}.\n     *\n     * @param obj The object instance for which the value has been stored.\n     * @param key The key in the value map for this object instance.\n     * @return The previously stored value for this instance/key combination, or {@code null} if there was none.\n     */\n    public static Object removeAdditionalInstanceField(Object obj, String key) {\n        if (obj == null)\n            throw new NullPointerException(\"object must not be null\");\n        if (key == null)\n            throw new NullPointerException(\"key must not be null\");\n\n        HashMap<String, Object> objectFields;\n        synchronized (additionalFields) {\n            objectFields = additionalFields.get(obj);\n            if (objectFields == null)\n                return null;\n        }\n\n        synchronized (objectFields) {\n            return objectFields.remove(key);\n        }\n    }\n\n    /**\n     * Like {@link #setAdditionalInstanceField}, but the value is stored for the class of {@code obj}.\n     */\n    public static Object setAdditionalStaticField(Object obj, String key, Object value) {\n        return setAdditionalInstanceField(obj.getClass(), key, value);\n    }\n\n    /**\n     * Like {@link #getAdditionalInstanceField}, but the value is returned for the class of {@code obj}.\n     */\n    public static Object getAdditionalStaticField(Object obj, String key) {\n        return getAdditionalInstanceField(obj.getClass(), key);\n    }\n\n    /**\n     * Like {@link #removeAdditionalInstanceField}, but the value is removed and returned for the class of {@code obj}.\n     */\n    public static Object removeAdditionalStaticField(Object obj, String key) {\n        return removeAdditionalInstanceField(obj.getClass(), key);\n    }\n\n    /**\n     * Like {@link #setAdditionalInstanceField}, but the value is stored for {@code clazz}.\n     */\n    public static Object setAdditionalStaticField(Class<?> clazz, String key, Object value) {\n        return setAdditionalInstanceField(clazz, key, value);\n    }\n\n    /**\n     * Like {@link #setAdditionalInstanceField}, but the value is returned for {@code clazz}.\n     */\n    public static Object getAdditionalStaticField(Class<?> clazz, String key) {\n        return getAdditionalInstanceField(clazz, key);\n    }\n\n    /**\n     * Like {@link #setAdditionalInstanceField}, but the value is removed and returned for {@code clazz}.\n     */\n    public static Object removeAdditionalStaticField(Class<?> clazz, String key) {\n        return removeAdditionalInstanceField(clazz, key);\n    }\n\n    //#################################################################################################\n\n    /**\n     * Loads an asset from a resource object and returns the content as {@code byte} array.\n     *\n     * @param res  The resources from which the asset should be loaded.\n     * @param path The path to the asset, as in {@link AssetManager#open}.\n     * @return The content of the asset.\n     */\n    public static byte[] assetAsByteArray(Resources res, String path) throws IOException {\n        return inputStreamToByteArray(res.getAssets().open(path));\n    }\n\n    /*package*/\n    static byte[] inputStreamToByteArray(InputStream is) throws IOException {\n        ByteArrayOutputStream buf = new ByteArrayOutputStream();\n        byte[] temp = new byte[1024];\n        int read;\n\n        while ((read = is.read(temp)) > 0) {\n            buf.write(temp, 0, read);\n        }\n        is.close();\n        return buf.toByteArray();\n    }\n\n    /**\n     * Returns the lowercase hex string representation of a file's MD5 hash sum.\n     */\n    public static String getMD5Sum(String file) throws IOException {\n        try {\n            MessageDigest digest = MessageDigest.getInstance(\"MD5\");\n            InputStream is = new FileInputStream(file);\n            byte[] buffer = new byte[8192];\n            int read;\n            while ((read = is.read(buffer)) > 0) {\n                digest.update(buffer, 0, read);\n            }\n            is.close();\n            byte[] md5sum = digest.digest();\n            BigInteger bigInt = new BigInteger(1, md5sum);\n            return bigInt.toString(16);\n        } catch (NoSuchAlgorithmException e) {\n            return \"\";\n        }\n    }\n\n    //#################################################################################################\n\n    /**\n     * Increments the depth counter for the given method.\n     *\n     * <p>The intention of the method depth counter is to keep track of the call depth for recursive\n     * methods, e.g. to override parameters only for the outer call. The Xposed framework uses this\n     * to load drawable replacements only once per call, even when multiple\n     * {@link Resources#getDrawable} variants call each other.\n     *\n     * @param method The method name. Should be prefixed with a unique, module-specific string.\n     * @return The updated depth.\n     */\n    public static int incrementMethodDepth(String method) {\n        return getMethodDepthCounter(method).get().incrementAndGet();\n    }\n\n    /**\n     * Decrements the depth counter for the given method.\n     * See {@link #incrementMethodDepth} for details.\n     *\n     * @param method The method name. Should be prefixed with a unique, module-specific string.\n     * @return The updated depth.\n     */\n    public static int decrementMethodDepth(String method) {\n        return getMethodDepthCounter(method).get().decrementAndGet();\n    }\n\n    /**\n     * Returns the current depth counter for the given method.\n     * See {@link #incrementMethodDepth} for details.\n     *\n     * @param method The method name. Should be prefixed with a unique, module-specific string.\n     * @return The updated depth.\n     */\n    public static int getMethodDepth(String method) {\n        return getMethodDepthCounter(method).get().get();\n    }\n\n    private static ThreadLocal<AtomicInteger> getMethodDepthCounter(String method) {\n        synchronized (sMethodDepth) {\n            ThreadLocal<AtomicInteger> counter = sMethodDepth.get(method);\n            if (counter == null) {\n                counter = new ThreadLocal<AtomicInteger>() {\n                    @Override\n                    protected AtomicInteger initialValue() {\n                        return new AtomicInteger();\n                    }\n                };\n                sMethodDepth.put(method, counter);\n            }\n            return counter;\n        }\n    }\n\n    //#################################################################################################\n    // TODO helpers for view traversing\n\t/*To make it easier, I will try and implement some more helpers:\n\t- add view before/after existing view (I already mentioned that I think)\n\t- get index of view in its parent\n\t- get next/previous sibling (maybe with an optional argument \"type\", that might be ImageView.class and gives you the next sibling that is an ImageView)?\n\t- get next/previous element (similar to the above, but would also work if the next element has a different parent, it would just go up the hierarchy and then down again until it finds a matching element)\n\t- find the first child that is an instance of a specified class\n\t- find all (direct or indirect) children of a specified class\n\t*/\n\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/XposedInit.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed;\n\nimport static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient;\nimport static org.lsposed.lspd.deopt.PrebuiltMethodsDeopter.deoptResourceMethods;\nimport static de.robv.android.xposed.XposedBridge.hookAllMethods;\nimport static de.robv.android.xposed.XposedHelpers.callMethod;\nimport static de.robv.android.xposed.XposedHelpers.findAndHookMethod;\nimport static de.robv.android.xposed.XposedHelpers.getObjectField;\nimport static de.robv.android.xposed.XposedHelpers.getParameterIndexByType;\nimport static de.robv.android.xposed.XposedHelpers.setStaticObjectField;\n\nimport android.app.ActivityThread;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.Resources;\nimport android.content.res.ResourcesImpl;\nimport android.content.res.TypedArray;\nimport android.content.res.XResources;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.Process;\nimport android.util.ArrayMap;\n\nimport org.lsposed.lspd.impl.LSPosedContext;\nimport org.lsposed.lspd.models.PreLoadedApk;\nimport org.matrix.vector.nativebridge.NativeAPI;\nimport org.matrix.vector.nativebridge.ResourcesHook;\nimport org.lsposed.lspd.util.LspModuleClassLoader;\nimport org.lsposed.lspd.util.Utils.Log;\n\nimport java.io.File;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport de.robv.android.xposed.callbacks.XC_InitPackageResources;\nimport de.robv.android.xposed.callbacks.XCallback;\nimport hidden.HiddenApiBridge;\n\npublic final class XposedInit {\n    private static final String TAG = XposedBridge.TAG;\n    public static boolean startsSystemServer = false;\n\n    public static volatile boolean disableResources = false;\n    public static AtomicBoolean resourceInit = new AtomicBoolean(false);\n\n    public static void hookResources() throws Throwable {\n        if (disableResources || !resourceInit.compareAndSet(false, true)) {\n            return;\n        }\n\n        deoptResourceMethods();\n\n        if (!ResourcesHook.initXResourcesNative()) {\n            Log.e(TAG, \"Cannot hook resources\");\n            disableResources = true;\n            return;\n        }\n\n        findAndHookMethod(\"android.app.ApplicationPackageManager\", null, \"getResourcesForApplication\",\n                ApplicationInfo.class, new XC_MethodHook() {\n                    @Override\n                    protected void beforeHookedMethod(MethodHookParam<?> param) {\n                        ApplicationInfo app = (ApplicationInfo) param.args[0];\n                        XResources.setPackageNameForResDir(app.packageName,\n                                app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir);\n                    }\n                });\n\n        /*\n         * getTopLevelResources(a)\n         *   -> getTopLevelResources(b)\n         *     -> key = new ResourcesKey()\n         *     -> r = new Resources()\n         *     -> mActiveResources.put(key, r)\n         *     -> return r\n         */\n\n        final Class<?> classGTLR;\n        final Class<?> classResKey;\n        final ThreadLocal<Object> latestResKey = new ThreadLocal<>();\n        final ArrayList<String> createResourceMethods = new ArrayList<>();\n\n        classGTLR = android.app.ResourcesManager.class;\n        classResKey = android.content.res.ResourcesKey.class;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n            createResourceMethods.add(\"createResources\");\n            createResourceMethods.add(\"createResourcesForActivity\");\n        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n            createResourceMethods.add(\"createResources\");\n        } else {\n            createResourceMethods.add(\"getOrCreateResources\");\n        }\n\n        final Class<?> classActivityRes = XposedHelpers.findClassIfExists(\"android.app.ResourcesManager$ActivityResource\", classGTLR.getClassLoader());\n        var hooker = new XC_MethodHook() {\n            @Override\n            protected void afterHookedMethod(MethodHookParam<?> param) {\n                // At least on OnePlus 5, the method has an additional parameter compared to AOSP.\n                Object activityToken = null;\n                try {\n                    final int activityTokenIdx = getParameterIndexByType(param.method, IBinder.class);\n                    activityToken = param.args[activityTokenIdx];\n                } catch (NoSuchFieldError ignored) {\n                }\n                final int resKeyIdx = getParameterIndexByType(param.method, classResKey);\n                String resDir = (String) getObjectField(param.args[resKeyIdx], \"mResDir\");\n                XResources newRes = cloneToXResources(param, resDir);\n                if (newRes == null) {\n                    return;\n                }\n\n                //noinspection SynchronizeOnNonFinalField\n                synchronized (param.thisObject) {\n                    ArrayList<Object> resourceReferences;\n                    if (activityToken != null) {\n                        Object activityResources = callMethod(param.thisObject, \"getOrCreateActivityResourcesStructLocked\", activityToken);\n                        //noinspection unchecked\n                        resourceReferences = (ArrayList<Object>) getObjectField(activityResources, \"activityResources\");\n                    } else {\n                        //noinspection unchecked\n                        resourceReferences = (ArrayList<Object>) getObjectField(param.thisObject, \"mResourceReferences\");\n                    }\n                    if (activityToken == null || classActivityRes == null) {\n                        resourceReferences.add(new WeakReference<>(newRes));\n                    } else {\n                        // Android S createResourcesForActivity()\n                        var activityRes = XposedHelpers.newInstance(classActivityRes);\n                        XposedHelpers.setObjectField(activityRes, \"resources\", new WeakReference<>(newRes));\n                        resourceReferences.add(activityRes);\n                    }\n                }\n            }\n        };\n\n        for (var createResourceMethod : createResourceMethods) {\n            hookAllMethods(classGTLR, createResourceMethod, hooker);\n        }\n\n        findAndHookMethod(TypedArray.class, \"obtain\", Resources.class, int.class,\n                new XC_MethodHook() {\n                    @Override\n                    protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {\n                        if (param.getResult() instanceof XResources.XTypedArray) {\n                            return;\n                        }\n                        if (!(param.args[0] instanceof XResources)) {\n                            return;\n                        }\n                        XResources.XTypedArray newResult =\n                                new XResources.XTypedArray((Resources) param.args[0]);\n                        int len = (int) param.args[1];\n                        Method resizeMethod = XposedHelpers.findMethodBestMatch(\n                                TypedArray.class, \"resize\", int.class);\n                        resizeMethod.setAccessible(true);\n                        resizeMethod.invoke(newResult, len);\n                        param.setResult(newResult);\n                    }\n                });\n\n        // Replace system resources\n        XResources systemRes = new XResources(\n                (ClassLoader) XposedHelpers.getObjectField(Resources.getSystem(), \"mClassLoader\"), null);\n        HiddenApiBridge.Resources_setImpl(systemRes, (ResourcesImpl) XposedHelpers.getObjectField(Resources.getSystem(), \"mResourcesImpl\"));\n        setStaticObjectField(Resources.class, \"mSystem\", systemRes);\n\n        XResources.init(latestResKey);\n    }\n\n    private static XResources cloneToXResources(XC_MethodHook.MethodHookParam<?> param, String resDir) {\n        Object result = param.getResult();\n        if (result == null || result instanceof XResources) {\n            return null;\n        }\n\n        // Replace the returned resources with our subclass.\n        var newRes = new XResources(\n                (ClassLoader) XposedHelpers.getObjectField(param.getResult(), \"mClassLoader\"), resDir);\n        HiddenApiBridge.Resources_setImpl(newRes, (ResourcesImpl) XposedHelpers.getObjectField(param.getResult(), \"mResourcesImpl\"));\n\n        // Invoke handleInitPackageResources().\n        if (newRes.isFirstLoad()) {\n            String packageName = newRes.getPackageName();\n            XC_InitPackageResources.InitPackageResourcesParam resparam = new XC_InitPackageResources.InitPackageResourcesParam(XposedBridge.sInitPackageResourcesCallbacks);\n            resparam.packageName = packageName;\n            resparam.res = newRes;\n            XCallback.callAll(resparam);\n        }\n\n        param.setResult(newRes);\n        return newRes;\n    }\n\n    // only legacy modules have non-empty value\n    private static final Map<String, Optional<String>> loadedModules = new ConcurrentHashMap<>();\n\n    public static Map<String, Optional<String>> getLoadedModules() {\n        return loadedModules;\n    }\n\n    public static void loadLegacyModules() {\n        var moduleList = serviceClient.getLegacyModulesList();\n        moduleList.forEach(module -> {\n            var apk = module.apkPath;\n            var name = module.packageName;\n            var file = module.file;\n            loadedModules.put(name, Optional.of(apk)); // temporarily add it for XSharedPreference\n            if (!loadModule(name, apk, file)) {\n                loadedModules.remove(name);\n            }\n        });\n    }\n\n    public static void loadModules(ActivityThread at) {\n        var packages = (ArrayMap<?, ?>) XposedHelpers.getObjectField(at, \"mPackages\");\n        serviceClient.getModulesList().forEach(module -> {\n            loadedModules.put(module.packageName, Optional.empty());\n            if (!LSPosedContext.loadModule(at, module)) {\n                loadedModules.remove(module.packageName);\n            } else {\n                packages.remove(module.packageName);\n            }\n        });\n    }\n\n    /**\n     * Load all so from an APK by reading <code>assets/native_init</code>.\n     * It will only store the so names but not doing anything.\n     */\n    private static void initNativeModule(List<String> moduleLibraryNames) {\n        moduleLibraryNames.forEach(NativeAPI::recordNativeEntrypoint);\n    }\n\n    private static boolean initModule(ClassLoader mcl, String apk, List<String> moduleClassNames) {\n        var count = 0;\n        for (var moduleClassName : moduleClassNames) {\n            try {\n                Log.i(TAG, \"  Loading class \" + moduleClassName);\n\n                Class<?> moduleClass = mcl.loadClass(moduleClassName);\n\n                if (!IXposedMod.class.isAssignableFrom(moduleClass)) {\n                    Log.e(TAG, \"    This class doesn't implement any sub-interface of IXposedMod, skipping it\");\n                    continue;\n                }\n\n                final Object moduleInstance = moduleClass.newInstance();\n\n                if (moduleInstance instanceof IXposedHookZygoteInit) {\n                    IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();\n                    param.modulePath = apk;\n                    param.startsSystemServer = startsSystemServer;\n                    ((IXposedHookZygoteInit) moduleInstance).initZygote(param);\n                    count++;\n                }\n\n                if (moduleInstance instanceof IXposedHookLoadPackage) {\n                    XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));\n                    count++;\n                }\n\n                if (moduleInstance instanceof IXposedHookInitPackageResources) {\n                    hookResources();\n                    XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));\n                    count++;\n                }\n            } catch (Throwable t) {\n                Log.e(TAG, \"    Failed to load class \" + moduleClassName, t);\n            }\n        }\n        return count > 0;\n    }\n\n    /**\n     * Load a module from an APK by calling the init(String) method for all classes defined\n     * in <code>assets/xposed_init</code>.\n     */\n    private static boolean loadModule(String name, String apk, PreLoadedApk file) {\n        Log.i(TAG, \"Loading legacy module \" + name + \" from \" + apk);\n\n        var sb = new StringBuilder();\n        var abis = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS;\n        for (String abi : abis) {\n            sb.append(apk).append(\"!/lib/\").append(abi).append(File.pathSeparator);\n        }\n        var librarySearchPath = sb.toString();\n\n        var initLoader = XposedInit.class.getClassLoader();\n        var mcl = LspModuleClassLoader.loadApk(apk, file.preLoadedDexes, librarySearchPath, initLoader);\n\n        try {\n            if (mcl.loadClass(XposedBridge.class.getName()).getClassLoader() != initLoader) {\n                Log.e(TAG, \"  Cannot load module: \" + name);\n                Log.e(TAG, \"  The Xposed API classes are compiled into the module's APK.\");\n                Log.e(TAG, \"  This may cause strange issues and must be fixed by the module developer.\");\n                Log.e(TAG, \"  For details, see: https://api.xposed.info/using.html\");\n                return false;\n            }\n        } catch (ClassNotFoundException ignored) {\n            return false;\n        }\n        initNativeModule(file.moduleLibraryNames);\n        return initModule(mcl, apk, file.moduleClassNames);\n    }\n\n    public final static Set<String> loadedPackagesInProcess = ConcurrentHashMap.newKeySet(1);\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/callbacks/IXUnhook.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.callbacks;\n\nimport de.robv.android.xposed.IXposedHookZygoteInit;\n\n/**\n * Interface for objects that can be used to remove callbacks.\n *\n * <p class=\"warning\">Just like hooking methods etc., unhooking applies only to the current process.\n * In other process (or when the app is removed from memory and then restarted), the hook will still\n * be active. The Zygote process (see {@link IXposedHookZygoteInit}) is an exception, the hook won't\n * be inherited by any future processes forked from it in the future.\n *\n * @param <T> The class of the callback.\n */\npublic interface IXUnhook<T> {\n    /**\n     * Returns the callback that has been registered.\n     */\n    T getCallback();\n\n    /**\n     * Removes the callback.\n     */\n    void unhook();\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/callbacks/XC_InitPackageResources.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.callbacks;\n\nimport android.content.res.XResources;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport de.robv.android.xposed.IXposedHookInitPackageResources;\nimport io.github.libxposed.api.XposedModuleInterface;\n\n/**\n * This class is only used for internal purposes, except for the {@link InitPackageResourcesParam}\n * subclass.\n */\npublic abstract class XC_InitPackageResources extends XCallback implements IXposedHookInitPackageResources {\n    /**\n     * Creates a new callback with default priority.\n     *\n     * @hide\n     */\n    @SuppressWarnings(\"deprecation\")\n    public XC_InitPackageResources() {\n        super();\n    }\n\n    /**\n     * Creates a new callback with a specific priority.\n     *\n     * @param priority See {@link XCallback#priority}.\n     * @hide\n     */\n    public XC_InitPackageResources(int priority) {\n        super(priority);\n    }\n\n    /**\n     * Wraps information about the resources being initialized.\n     */\n    public static final class InitPackageResourcesParam extends XCallback.Param {\n        /**\n         * @hide\n         */\n        public InitPackageResourcesParam(CopyOnWriteArraySet<XC_InitPackageResources> callbacks) {\n            super(callbacks.toArray(new XCallback[0]));\n        }\n\n        /**\n         * The name of the package for which resources are being loaded.\n         */\n        public String packageName;\n\n        /**\n         * Reference to the resources that can be used for calls to\n         * {@link XResources#setReplacement(String, String, String, Object)}.\n         */\n        public XResources res;\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    protected void call(Param param) throws Throwable {\n        if (param instanceof InitPackageResourcesParam)\n            handleInitPackageResources((InitPackageResourcesParam) param);\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/callbacks/XC_LayoutInflated.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.callbacks;\n\nimport android.content.res.XResources;\nimport android.content.res.XResources.ResourceNames;\nimport android.view.View;\n\nimport de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;\n\n/**\n * Callback for hooking layouts. Such callbacks can be passed to {@link XResources#hookLayout}\n * and its variants.\n */\npublic abstract class XC_LayoutInflated extends XCallback implements Comparable<XC_LayoutInflated> {\n    /**\n     * Creates a new callback with default priority.\n     */\n    @SuppressWarnings(\"deprecation\")\n    public XC_LayoutInflated() {\n        super();\n    }\n\n    /**\n     * Creates a new callback with a specific priority.\n     *\n     * @param priority See {@link XCallback#priority}.\n     */\n    public XC_LayoutInflated(int priority) {\n        super(priority);\n    }\n\n    /**\n     * Wraps information about the inflated layout.\n     */\n    public static final class LayoutInflatedParam extends XCallback.Param {\n        /**\n         * @hide\n         */\n        public LayoutInflatedParam(CopyOnWriteSortedSet<XC_LayoutInflated> callbacks) {\n            super(callbacks.getSnapshot(new XCallback[0]));\n        }\n\n        /**\n         * The view that has been created from the layout.\n         */\n        public View view;\n\n        /**\n         * Container with the ID and name of the underlying resource.\n         */\n        public ResourceNames resNames;\n\n        /**\n         * Directory from which the layout was actually loaded (e.g. \"layout-sw600dp\").\n         */\n        public String variant;\n\n        /**\n         * Resources containing the layout.\n         */\n        public XResources res;\n    }\n\n    /** @hide */\n    @Override\n    public int compareTo(XC_LayoutInflated other) {\n        if (this == other)\n            return 0;\n\n        // order descending by priority\n        if (other.priority != this.priority)\n            return other.priority - this.priority;\n            // then randomly\n        else if (System.identityHashCode(this) < System.identityHashCode(other))\n            return -1;\n        else\n            return 1;\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    protected void call(Param param) throws Throwable {\n        if (param instanceof LayoutInflatedParam)\n            handleLayoutInflated((LayoutInflatedParam) param);\n    }\n\n    /**\n     * This method is called when the hooked layout has been inflated.\n     *\n     * @param liparam Information about the layout and the inflated view.\n     * @throws Throwable Everything the callback throws is caught and logged.\n     */\n    public abstract void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable;\n\n    /**\n     * An object with which the callback can be removed.\n     */\n    public class Unhook implements IXUnhook<XC_LayoutInflated> {\n        private final String resDir;\n        private final int id;\n\n        /**\n         * @hide\n         */\n        public Unhook(String resDir, int id) {\n            this.resDir = resDir;\n            this.id = id;\n        }\n\n        /**\n         * Returns the resource ID of the hooked layout.\n         */\n        public int getId() {\n            return id;\n        }\n\n        @Override\n        public XC_LayoutInflated getCallback() {\n            return XC_LayoutInflated.this;\n        }\n\n        @Override\n        public void unhook() {\n            XResources.unhookLayout(resDir, id, XC_LayoutInflated.this);\n        }\n\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.callbacks;\n\nimport android.content.pm.ApplicationInfo;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport de.robv.android.xposed.IXposedHookLoadPackage;\nimport de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;\nimport io.github.libxposed.api.XposedModuleInterface;\n\n/**\n * This class is only used for internal purposes, except for the {@link LoadPackageParam}\n * subclass.\n */\npublic abstract class XC_LoadPackage extends XCallback implements IXposedHookLoadPackage {\n    /**\n     * Creates a new callback with default priority.\n     *\n     * @hide\n     */\n    @SuppressWarnings(\"deprecation\")\n    public XC_LoadPackage() {\n        super();\n    }\n\n    /**\n     * Creates a new callback with a specific priority.\n     *\n     * @param priority See {@link XCallback#priority}.\n     * @hide\n     */\n    public XC_LoadPackage(int priority) {\n        super(priority);\n    }\n\n    /**\n     * Wraps information about the app being loaded.\n     */\n    public static final class LoadPackageParam extends XCallback.Param {\n        /**\n         * @hide\n         */\n        public LoadPackageParam(CopyOnWriteArraySet<XC_LoadPackage> callbacks) {\n            super(callbacks.toArray(new XCallback[0]));\n        }\n\n        /**\n         * The name of the package being loaded.\n         */\n        public String packageName;\n\n        /**\n         * The process in which the package is executed.\n         */\n        public String processName;\n\n        /**\n         * The ClassLoader used for this package.\n         */\n        public ClassLoader classLoader;\n\n        /**\n         * More information about the application being loaded.\n         */\n        public ApplicationInfo appInfo;\n\n        /**\n         * Set to {@code true} if this is the first (and main) application for this process.\n         */\n        public boolean isFirstApplication;\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    protected void call(Param param) throws Throwable {\n        if (param instanceof LoadPackageParam)\n            handleLoadPackage((LoadPackageParam) param);\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/callbacks/XCallback.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.callbacks;\n\nimport android.os.Bundle;\n\nimport org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;\n\nimport java.io.Serializable;\n\nimport de.robv.android.xposed.XposedBridge;\n\n/**\n * Base class for Xposed callbacks.\n * <p>\n * This class only keeps a priority for ordering multiple callbacks.\n * The actual (abstract) callback methods are added by subclasses.\n */\nabstract public class XCallback {\n    /**\n     * Callback priority, higher number means earlier execution.\n     *\n     * <p>This is usually set to {@link #PRIORITY_DEFAULT}. However, in case a certain callback should\n     * be executed earlier or later a value between {@link #PRIORITY_HIGHEST} and {@link #PRIORITY_LOWEST}\n     * can be set instead. The values are just for orientation though, Xposed doesn't enforce any\n     * boundaries on the priority values.\n     */\n    public final int priority;\n\n    /**\n     * @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!\n     */\n    @Deprecated\n    public XCallback() {\n        this.priority = PRIORITY_DEFAULT;\n    }\n\n    /**\n     * @hide\n     */\n    public XCallback(int priority) {\n        this.priority = priority;\n    }\n\n    /**\n     * Base class for Xposed callback parameters.\n     */\n    public static abstract class Param {\n        /**\n         * @hide\n         */\n        public final XCallback[] callbacks;\n        private Bundle extra;\n\n        /**\n         * @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!\n         */\n        @Deprecated\n        protected Param() {\n            callbacks = null;\n        }\n\n        /**\n         * @hide\n         */\n        protected Param(XCallback[] callbacks) {\n            this.callbacks = callbacks;\n        }\n\n        /**\n         * This can be used to store any data for the scope of the callback.\n         *\n         * <p>Use this instead of instance variables, as it has a clear reference to e.g. each\n         * separate call to a method, even when the same method is called recursively.\n         *\n         * @see #setObjectExtra\n         * @see #getObjectExtra\n         */\n        public synchronized Bundle getExtra() {\n            if (extra == null)\n                extra = new Bundle();\n            return extra;\n        }\n\n        /**\n         * Returns an object stored with {@link #setObjectExtra}.\n         */\n        public Object getObjectExtra(String key) {\n            Serializable o = getExtra().getSerializable(key);\n            if (o instanceof SerializeWrapper)\n                return ((SerializeWrapper) o).object;\n            return null;\n        }\n\n        /**\n         * Stores any object for the scope of the callback. For data types that support it, use\n         * the {@link Bundle} returned by {@link #getExtra} instead.\n         */\n        public void setObjectExtra(String key, Object o) {\n            getExtra().putSerializable(key, new SerializeWrapper(o));\n        }\n\n        private static class SerializeWrapper implements Serializable {\n            private static final long serialVersionUID = 1L;\n            private final Object object;\n\n            public SerializeWrapper(Object o) {\n                object = o;\n            }\n        }\n    }\n\n    /**\n     * @hide\n     */\n    public static void callAll(Param param) {\n\n        if (param instanceof XC_LoadPackage.LoadPackageParam) {\n            // deopt methods in system apps or priv-apps, this would be not necessary\n            // only if we found out how to recompile their apks\n            XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param;\n            PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader);\n        }\n\n        if (param.callbacks == null)\n            throw new IllegalStateException(\"This object was not created for use with callAll\");\n\n        for (int i = 0; i < param.callbacks.length; i++) {\n            try {\n                param.callbacks[i].call(param);\n            } catch (Throwable t) {\n                XposedBridge.log(t);\n            }\n        }\n    }\n\n    /**\n     * @hide\n     */\n    protected void call(Param param) throws Throwable {\n    }\n\n\n    /**\n     * The default priority, see {@link #priority}.\n     */\n    public static final int PRIORITY_DEFAULT = 50;\n\n    /**\n     * Execute this callback late, see {@link #priority}.\n     */\n    public static final int PRIORITY_LOWEST = -10000;\n\n    /**\n     * Execute this callback early, see {@link #priority}.\n     */\n    public static final int PRIORITY_HIGHEST = 10000;\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/services/BaseService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.services;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport de.robv.android.xposed.SELinuxHelper;\n\n/**\n * General definition of a file access service provided by the Xposed framework.\n *\n * <p>References to a concrete subclass should generally be retrieved from {@link SELinuxHelper}.\n */\npublic abstract class BaseService {\n    /**\n     * Flag for {@link #checkFileAccess}: Read access.\n     */\n    public static final int R_OK = 4;\n    /**\n     * Flag for {@link #checkFileAccess}: Write access.\n     */\n    public static final int W_OK = 2;\n    /**\n     * Flag for {@link #checkFileAccess}: Executable access.\n     */\n    public static final int X_OK = 1;\n    /**\n     * Flag for {@link #checkFileAccess}: File/directory exists.\n     */\n    public static final int F_OK = 0;\n\n    /**\n     * Checks whether the services accesses files directly (instead of using IPC).\n     *\n     * @return {@code true} in case direct access is possible.\n     */\n    public boolean hasDirectFileAccess() {\n        return false;\n    }\n\n    /**\n     * Check whether a file is accessible. SELinux might enforce stricter checks.\n     *\n     * @param filename The absolute path of the file to check.\n     * @param mode     The mode for POSIX's {@code access()} function.\n     * @return The result of the {@code access()} function.\n     */\n    public abstract boolean checkFileAccess(String filename, int mode);\n\n    /**\n     * Check whether a file exists.\n     *\n     * @param filename The absolute path of the file to check.\n     * @return The result of the {@code access()} function.\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    public boolean checkFileExists(String filename) {\n        return checkFileAccess(filename, F_OK);\n    }\n\n    /**\n     * Determine the size and modification time of a file.\n     *\n     * @param filename The absolute path of the file to check.\n     * @return A {@link FileResult} object holding the result.\n     * @throws IOException In case an error occurred while retrieving the information.\n     */\n    public abstract FileResult statFile(String filename) throws IOException;\n\n    /**\n     * Determine the size time of a file.\n     *\n     * @param filename The absolute path of the file to check.\n     * @return The file size.\n     * @throws IOException In case an error occurred while retrieving the information.\n     */\n    public long getFileSize(String filename) throws IOException {\n        return statFile(filename).size;\n    }\n\n    /**\n     * Determine the size time of a file.\n     *\n     * @param filename The absolute path of the file to check.\n     * @return The file modification time.\n     * @throws IOException In case an error occurred while retrieving the information.\n     */\n    public long getFileModificationTime(String filename) throws IOException {\n        return statFile(filename).mtime;\n    }\n\n    /**\n     * Read a file into memory.\n     *\n     * @param filename The absolute path of the file to read.\n     * @return A {@code byte} array with the file content.\n     * @throws IOException In case an error occurred while reading the file.\n     */\n    public abstract byte[] readFile(String filename) throws IOException;\n\n    /**\n     * Read a file into memory, but only if it has changed since the last time.\n     *\n     * @param filename     The absolute path of the file to read.\n     * @param previousSize File size of last read.\n     * @param previousTime File modification time of last read.\n     * @return A {@link FileResult} object holding the result.\n     * <p>The {@link FileResult#content} field might be {@code null} if the file\n     * is unmodified ({@code previousSize} and {@code previousTime} are still valid).\n     * @throws IOException In case an error occurred while reading the file.\n     */\n    public abstract FileResult readFile(String filename, long previousSize, long previousTime) throws IOException;\n\n    /**\n     * Read a file into memory, optionally only if it has changed since the last time.\n     *\n     * @param filename     The absolute path of the file to read.\n     * @param offset       Number of bytes to skip at the beginning of the file.\n     * @param length       Number of bytes to read (0 means read to end of file).\n     * @param previousSize Optional: File size of last read.\n     * @param previousTime Optional: File modification time of last read.\n     * @return A {@link FileResult} object holding the result.\n     * <p>The {@link FileResult#content} field might be {@code null} if the file\n     * is unmodified ({@code previousSize} and {@code previousTime} are still valid).\n     * @throws IOException In case an error occurred while reading the file.\n     */\n    public abstract FileResult readFile(String filename, int offset, int length,\n                                        long previousSize, long previousTime) throws IOException;\n\n    /**\n     * Get a stream to the file content.\n     * Depending on the service, it may or may not be read completely into memory.\n     *\n     * @param filename The absolute path of the file to read.\n     * @return An {@link InputStream} to the file content.\n     * @throws IOException In case an error occurred while reading the file.\n     */\n    public InputStream getFileInputStream(String filename) throws IOException {\n        return new ByteArrayInputStream(readFile(filename));\n    }\n\n    /**\n     * Get a stream to the file content, but only if it has changed since the last time.\n     * Depending on the service, it may or may not be read completely into memory.\n     *\n     * @param filename     The absolute path of the file to read.\n     * @param previousSize Optional: File size of last read.\n     * @param previousTime Optional: File modification time of last read.\n     * @return A {@link FileResult} object holding the result.\n     * <p>The {@link FileResult#stream} field might be {@code null} if the file\n     * is unmodified ({@code previousSize} and {@code previousTime} are still valid).\n     * @throws IOException In case an error occurred while reading the file.\n     */\n    public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {\n        FileResult result = readFile(filename, previousSize, previousTime);\n        if (result.content == null)\n            return result;\n        return new FileResult(new ByteArrayInputStream(result.content), result.size, result.mtime);\n    }\n\n\n    // ----------------------------------------------------------------------------\n    /*package*/ BaseService() {\n    }\n\n    /*package*/\n    static void ensureAbsolutePath(String filename) {\n        if (!filename.startsWith(\"/\")) {\n            throw new IllegalArgumentException(\"Only absolute filenames are allowed: \" + filename);\n        }\n    }\n\n    /*package*/\n    static void throwCommonIOException(int errno, String errorMsg, String filename, String defaultText) throws IOException {\n        switch (errno) {\n            case 1: // EPERM\n            case 13: // EACCES\n                throw new FileNotFoundException(errorMsg != null ? errorMsg : \"Permission denied: \" + filename);\n            case 2: // ENOENT\n                throw new FileNotFoundException(errorMsg != null ? errorMsg : \"No such file or directory: \" + filename);\n            case 12: // ENOMEM\n                throw new OutOfMemoryError(errorMsg);\n            case 21: // EISDIR\n                throw new FileNotFoundException(errorMsg != null ? errorMsg : \"Is a directory: \" + filename);\n            default:\n                throw new IOException(errorMsg != null ? errorMsg : \"Error \" + errno + defaultText + filename);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/services/DirectAccessService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.services;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @hide\n */\npublic final class DirectAccessService extends BaseService {\n    @Override\n    public boolean hasDirectFileAccess() {\n        return true;\n    }\n\n    @SuppressWarnings(\"RedundantIfStatement\")\n    @Override\n    public boolean checkFileAccess(String filename, int mode) {\n        File file = new File(filename);\n        if (mode == F_OK && !file.exists()) return false;\n        if ((mode & R_OK) != 0 && !file.canRead()) return false;\n        if ((mode & W_OK) != 0 && !file.canWrite()) return false;\n        if ((mode & X_OK) != 0 && !file.canExecute()) return false;\n        return true;\n    }\n\n    @Override\n    public boolean checkFileExists(String filename) {\n        return new File(filename).exists();\n    }\n\n    @Override\n    public FileResult statFile(String filename) throws IOException {\n        File file = new File(filename);\n        return new FileResult(file.length(), file.lastModified());\n    }\n\n    @Override\n    public byte[] readFile(String filename) throws IOException {\n        File file = new File(filename);\n        byte content[] = new byte[(int) file.length()];\n        FileInputStream fis = new FileInputStream(file);\n        fis.read(content);\n        fis.close();\n        return content;\n    }\n\n    @Override\n    public FileResult readFile(String filename, long previousSize, long previousTime) throws IOException {\n        File file = new File(filename);\n        long size = file.length();\n        long time = file.lastModified();\n        if (previousSize == size && previousTime == time)\n            return new FileResult(size, time);\n        return new FileResult(readFile(filename), size, time);\n    }\n\n    @Override\n    public FileResult readFile(String filename, int offset, int length, long previousSize, long previousTime) throws IOException {\n        File file = new File(filename);\n        long size = file.length();\n        long time = file.lastModified();\n        if (previousSize == size && previousTime == time)\n            return new FileResult(size, time);\n\n        // Shortcut for the simple case\n        if (offset <= 0 && length <= 0)\n            return new FileResult(readFile(filename), size, time);\n\n        // Check range\n        if (offset > 0 && offset >= size) {\n            throw new IllegalArgumentException(\"Offset \" + offset + \" is out of range for \" + filename);\n        } else if (offset < 0) {\n            offset = 0;\n        }\n\n        if (length > 0 && (offset + length) > size) {\n            throw new IllegalArgumentException(\"Length \" + length + \" is out of range for \" + filename);\n        } else if (length <= 0) {\n            length = (int) (size - offset);\n        }\n\n        byte content[] = new byte[length];\n        FileInputStream fis = new FileInputStream(file);\n        fis.skip(offset);\n        fis.read(content);\n        fis.close();\n        return new FileResult(content, size, time);\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p>This implementation returns a BufferedInputStream instead of loading the file into memory.\n     */\n    @Override\n    public InputStream getFileInputStream(String filename) throws IOException {\n        return new BufferedInputStream(new FileInputStream(filename), 16 * 1024);\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p>This implementation returns a BufferedInputStream instead of loading the file into memory.\n     */\n    @Override\n    public FileResult getFileInputStream(String filename, long previousSize, long previousTime) throws IOException {\n        File file = new File(filename);\n        long size = file.length();\n        long time = file.lastModified();\n        if (previousSize == size && previousTime == time)\n            return new FileResult(size, time);\n        return new FileResult(new BufferedInputStream(new FileInputStream(filename), 16 * 1024), size, time);\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/de/robv/android/xposed/services/FileResult.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage de.robv.android.xposed.services;\n\nimport java.io.InputStream;\n\n/**\n * Holder for the result of a {@link BaseService#readFile} or  {@link BaseService#statFile} call.\n */\npublic final class FileResult {\n    /**\n     * File content, might be {@code null} if the file wasn't read.\n     */\n    public final byte[] content;\n    /**\n     * File input stream, might be {@code null} if the file wasn't read.\n     */\n    public final InputStream stream;\n    /**\n     * File size.\n     */\n    public final long size;\n    /**\n     * File last modification time.\n     */\n    public final long mtime;\n\n    /*package*/ FileResult(long size, long mtime) {\n        this.content = null;\n        this.stream = null;\n        this.size = size;\n        this.mtime = mtime;\n    }\n\n    /*package*/ FileResult(byte[] content, long size, long mtime) {\n        this.content = content;\n        this.stream = null;\n        this.size = size;\n        this.mtime = mtime;\n    }\n\n    /*package*/ FileResult(InputStream stream, long size, long mtime) {\n        this.content = null;\n        this.stream = stream;\n        this.size = size;\n        this.mtime = mtime;\n    }\n\n    /**\n     * @hide\n     */\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder(\"{\");\n        if (content != null) {\n            sb.append(\"content.length: \");\n            sb.append(content.length);\n            sb.append(\", \");\n        }\n        if (stream != null) {\n            sb.append(\"stream: \");\n            sb.append(stream.toString());\n            sb.append(\", \");\n        }\n        sb.append(\"size: \");\n        sb.append(size);\n        sb.append(\", mtime: \");\n        sb.append(mtime);\n        sb.append(\"}\");\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.core;\n\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.ParcelFileDescriptor;\nimport android.os.RemoteException;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.lspd.models.Module;\nimport org.lsposed.lspd.service.ILSPApplicationService;\nimport org.lsposed.lspd.util.Utils;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic class ApplicationServiceClient implements ILSPApplicationService, IBinder.DeathRecipient {\n    public static ApplicationServiceClient serviceClient = null;\n\n    final ILSPApplicationService service;\n\n    final String processName;\n\n    private ApplicationServiceClient(@NonNull ILSPApplicationService service, @NonNull String processName) throws RemoteException {\n        this.service = service;\n        this.processName = processName;\n        this.service.asBinder().linkToDeath(this, 0);\n    }\n\n    synchronized static void Init(ILSPApplicationService service, String niceName) {\n        var binder = service.asBinder();\n        if (serviceClient == null && binder != null) {\n            try {\n                serviceClient = new ApplicationServiceClient(service, niceName);\n            } catch (RemoteException e) {\n                Utils.logE(\"link to death error: \", e);\n            }\n        }\n    }\n\n    @Override\n    public boolean isLogMuted() {\n        try {\n            return service.isLogMuted();\n        } catch (RemoteException | NullPointerException ignored) {\n        }\n        return false;\n    }\n\n    @Override\n    public List<Module> getLegacyModulesList() {\n        try {\n            return service.getLegacyModulesList();\n        } catch (RemoteException | NullPointerException ignored) {\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<Module> getModulesList() {\n        try {\n            return service.getModulesList();\n        } catch (RemoteException | NullPointerException ignored) {\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public String getPrefsPath(String packageName) {\n        try {\n            return service.getPrefsPath(packageName);\n        } catch (RemoteException | NullPointerException ignored) {\n        }\n        return null;\n    }\n\n    @Override\n    public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) {\n        try {\n            return service.requestInjectedManagerBinder(binder);\n        } catch (RemoteException | NullPointerException ignored) {\n        }\n        return null;\n    }\n\n    @Override\n    public IBinder asBinder() {\n        return service.asBinder();\n    }\n\n    @Override\n    public void binderDied() {\n        service.asBinder().unlinkToDeath(this, 0);\n        serviceClient = null;\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/core/Startup.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 - 2022 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.core;\n\nimport android.app.ActivityThread;\nimport android.app.LoadedApk;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.CompatibilityInfo;\nimport android.os.IBinder;\n\nimport com.android.internal.os.ZygoteInit;\n\nimport org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;\nimport org.lsposed.lspd.hooker.AttachHooker;\nimport org.lsposed.lspd.hooker.CrashDumpHooker;\nimport org.lsposed.lspd.hooker.HandleSystemServerProcessHooker;\nimport org.lsposed.lspd.hooker.LoadedApkCtorHooker;\nimport org.lsposed.lspd.hooker.LoadedApkCreateCLHooker;\nimport org.lsposed.lspd.hooker.OpenDexFileHooker;\nimport org.lsposed.lspd.hooker.StartBootstrapServicesHooker;\nimport org.lsposed.lspd.impl.LSPosedContext;\nimport org.lsposed.lspd.impl.LSPosedHelper;\nimport org.lsposed.lspd.service.ILSPApplicationService;\nimport org.lsposed.lspd.util.Utils;\n\nimport java.util.List;\n\nimport dalvik.system.DexFile;\nimport de.robv.android.xposed.XposedBridge;\nimport de.robv.android.xposed.XposedInit;\n\npublic class Startup {\n    private static void startBootstrapHook(boolean isSystem) {\n        Utils.logD(\"startBootstrapHook starts: isSystem = \" + isSystem);\n        LSPosedHelper.hookMethod(CrashDumpHooker.class, Thread.class, \"dispatchUncaughtException\", Throwable.class);\n        if (isSystem) {\n            LSPosedHelper.hookAllMethods(HandleSystemServerProcessHooker.class, ZygoteInit.class, \"handleSystemServerProcess\");\n        } else {\n            LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, \"openDexFile\");\n            LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, \"openInMemoryDexFile\");\n            LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, \"openInMemoryDexFiles\");\n        }\n        LSPosedHelper.hookConstructor(LoadedApkCtorHooker.class, LoadedApk.class,\n                ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,\n                ClassLoader.class, boolean.class, boolean.class, boolean.class);\n        LSPosedHelper.hookMethod(LoadedApkCreateCLHooker.class, LoadedApk.class, \"createOrUpdateClassLoaderLocked\", List.class);\n        LSPosedHelper.hookAllMethods(AttachHooker.class, ActivityThread.class, \"attach\");\n    }\n\n    public static void bootstrapXposed(boolean systemServerStarted) {\n        // Initialize the Xposed framework\n        try {\n            startBootstrapHook(XposedInit.startsSystemServer);\n            XposedInit.loadLegacyModules();\n        } catch (Throwable t) {\n            Utils.logE(\"error during Xposed initialization\", t);\n        }\n\n        if (systemServerStarted) {\n            Utils.logD(\"Manually triggering system_server module load for late injection\");\n\n            IBinder activityService = android.os.ServiceManager.getService(\"activity\");\n            if (activityService == null) {\n                Utils.logE(\"Activity service not found! Cannot get SystemServer ClassLoader.\");\n                return;\n            }\n\n            // Maintain state consistency for the rest of the Vector framework\n            HandleSystemServerProcessHooker.systemServerCL = activityService.getClass().getClassLoader();\n            HandleSystemServerProcessHooker.after();\n            StartBootstrapServicesHooker.before();\n\n            Utils.logI(\"Late system_server injection successfully completed.\");\n        }\n    }\n\n    public static void initXposed(boolean isSystem, String processName, String appDir, ILSPApplicationService service) {\n        // init logger\n        ApplicationServiceClient.Init(service, processName);\n        XposedBridge.initXResources();\n        XposedInit.startsSystemServer = isSystem;\n        LSPosedContext.isSystemServer = isSystem;\n        LSPosedContext.appDir = appDir;\n        LSPosedContext.processName = processName;\n        PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/deopt/InlinedMethodCallers.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.deopt;\n\nimport android.app.Instrumentation;\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.AssetManager;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\n\nimport java.util.HashMap;\n\n/**\n * Providing a whitelist of methods which are the callers of the target methods we want to hook.\n * Because the target methods are inlined into the callers, we deoptimize the callers to\n * run in intercept mode to make target methods hookable.\n * <p>\n * Only for methods which are included in pre-compiled framework codes.\n * TODO recompile system apps and priv-apps since their original dex files are available\n */\npublic class InlinedMethodCallers {\n\n    public static final String KEY_BOOT_IMAGE = \"boot_image\";\n    public static final String KEY_BOOT_IMAGE_MIUI_RES = \"boot_image_miui_res\";\n    public static final String KEY_SYSTEM_SERVER = \"system_server\";\n\n    /**\n     * Key should be {@link #KEY_BOOT_IMAGE}, {@link #KEY_SYSTEM_SERVER}, or a package name\n     * of system apps or priv-apps i.e. com.android.systemui\n     */\n    private static final HashMap<String, Object[][]> CALLERS = new HashMap<>();\n\n    /**\n     * format for each row: {className, methodName, methodSig}\n     */\n    private static final Object[][] BOOT_IMAGE = {\n            // callers of Application#attach(Context)\n            {\"android.app.Instrumentation\", \"newApplication\", ClassLoader.class, String.class, Context.class},\n            {\"android.app.Instrumentation\", \"newApplication\", ClassLoader.class, Context.class},\n\n            // callers of Instrumentation#newApplication(ClassLoader, String, Context)\n            {\"android.app.LoadedApk\", \"makeApplicationInner\", Boolean.TYPE, Instrumentation.class, Boolean.TYPE},\n            {\"android.app.LoadedApk\", \"makeApplicationInner\", Boolean.TYPE, Instrumentation.class},\n            {\"android.app.LoadedApk\", \"makeApplication\", Boolean.TYPE, Instrumentation.class},\n\n            {\"android.app.ContextImpl\", \"getSharedPreferencesPath\", String.class}\n    };\n\n    // TODO deprecate this\n    private static final Object[][] BOOT_IMAGE_FOR_MIUI_RES = {\n            // for MIUI resources hooking\n            {\"android.content.res.MiuiResources\", \"init\", String.class},\n            {\"android.content.res.MiuiResources\", \"updateMiuiImpl\"},\n            {\"android.content.res.MiuiResources\", \"setImpl\", \"android.content.res.ResourcesImpl\"},\n            {\"android.content.res.MiuiResources\", \"loadOverlayValue\", TypedValue.class, int.class},\n            {\"android.content.res.MiuiResources\", \"getThemeString\", CharSequence.class},\n            {\"android.content.res.MiuiResources\", \"<init>\", ClassLoader.class},\n            {\"android.content.res.MiuiResources\", \"<init>\"},\n            {\"android.content.res.MiuiResources\", \"<init>\", AssetManager.class, DisplayMetrics.class, Configuration.class},\n            {\"android.miui.ResourcesManager\", \"initMiuiResource\", Resources.class, String.class},\n            {\"android.app.LoadedApk\", \"getResources\", Resources.class},\n            {\"android.content.res.Resources\", \"getSystem\", Resources.class},\n            {\"android.app.ApplicationPackageManager\", \"getResourcesForApplication\", ApplicationInfo.class},\n            {\"android.app.ContextImpl\", \"setResources\", Resources.class},\n    };\n\n    private static final Object[][] SYSTEM_SERVER = {};\n\n    private static final Object[][] SYSTEM_UI = {};\n\n    static {\n        CALLERS.put(KEY_BOOT_IMAGE, BOOT_IMAGE);\n        CALLERS.put(KEY_BOOT_IMAGE_MIUI_RES, BOOT_IMAGE_FOR_MIUI_RES);\n        CALLERS.put(KEY_SYSTEM_SERVER, SYSTEM_SERVER);\n        CALLERS.put(\"com.android.systemui\", SYSTEM_UI);\n    }\n\n    public static HashMap<String, Object[][]> getAll() {\n        return CALLERS;\n    }\n\n    public static Object[][] get(String where) {\n        return CALLERS.get(where);\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/deopt/PrebuiltMethodsDeopter.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.deopt;\n\nimport static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_BOOT_IMAGE;\nimport static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_BOOT_IMAGE_MIUI_RES;\nimport static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_SYSTEM_SERVER;\n\nimport org.matrix.vector.nativebridge.HookBridge;\nimport org.lsposed.lspd.util.Hookers;\nimport org.lsposed.lspd.util.Utils;\n\nimport java.lang.reflect.Executable;\nimport java.util.Arrays;\n\nimport de.robv.android.xposed.XposedHelpers;\n\npublic class PrebuiltMethodsDeopter {\n\n    public static void deoptMethods(String where, ClassLoader cl) {\n        Object[][] callers = InlinedMethodCallers.get(where);\n        if (callers == null) {\n            return;\n        }\n        for (Object[] caller : callers) {\n            try {\n                if (caller.length < 2) continue;\n                if (!(caller[0] instanceof String)) continue;\n                if (!(caller[1] instanceof String)) continue;\n                Executable method;\n                Object[] params = new Object[caller.length - 2];\n                System.arraycopy(caller, 2, params, 0, params.length);\n                if (\"<init>\".equals(caller[1])) {\n                    method = XposedHelpers.findConstructorExactIfExists((String) caller[0], cl, params);\n                } else {\n                    method = XposedHelpers.findMethodExactIfExists((String) caller[0], cl, (String) caller[1], params);\n                }\n                if (method != null) {\n                    Hookers.logD(\"deoptimizing \" + method);\n                    HookBridge.deoptimizeMethod(method);\n                }\n            } catch (Throwable throwable) {\n                Utils.logE(\"error when deopting method: \" + Arrays.toString(caller), throwable);\n            }\n        }\n    }\n\n    public static void deoptBootMethods() {\n        // todo check if has been done before\n        deoptMethods(KEY_BOOT_IMAGE, null);\n    }\n\n    public static void deoptResourceMethods() {\n        if (Utils.isMIUI) {\n            //deopt these only for MIUI\n            deoptMethods(KEY_BOOT_IMAGE_MIUI_RES, null);\n        }\n    }\n\n    public static void deoptSystemServerMethods(ClassLoader sysCL) {\n        deoptMethods(KEY_SYSTEM_SERVER, sysCL);\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java",
    "content": "package org.lsposed.lspd.hooker;\n\nimport android.app.ActivityThread;\n\nimport de.robv.android.xposed.XposedInit;\nimport io.github.libxposed.api.XposedInterface;\n\npublic class AttachHooker implements XposedInterface.Hooker {\n\n    public static void after(XposedInterface.AfterHookCallback callback) {\n        XposedInit.loadModules((ActivityThread) callback.getThisObject());\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/CrashDumpHooker.java",
    "content": "package org.lsposed.lspd.hooker;\n\nimport org.lsposed.lspd.impl.LSPosedBridge;\nimport org.lsposed.lspd.util.Utils.Log;\n\nimport io.github.libxposed.api.XposedInterface;\n\npublic class CrashDumpHooker implements XposedInterface.Hooker {\n\n    public static void before(XposedInterface.BeforeHookCallback callback) {\n        try {\n            var e = (Throwable) callback.getArgs()[0];\n            LSPosedBridge.log(\"Crash unexpectedly: \" + Log.getStackTraceString(e));\n        } catch (Throwable ignored) {\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/HandleSystemServerProcessHooker.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.hooker;\n\nimport android.annotation.SuppressLint;\n\nimport org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;\nimport org.lsposed.lspd.impl.LSPosedHelper;\nimport org.lsposed.lspd.util.Hookers;\n\nimport io.github.libxposed.api.XposedInterface;\n\n// system_server initialization\npublic class HandleSystemServerProcessHooker implements XposedInterface.Hooker {\n\n    public interface Callback {\n        void onSystemServerLoaded(ClassLoader classLoader);\n    }\n\n    public static volatile ClassLoader systemServerCL = null;\n    public static volatile Callback callback = null;\n\n    @SuppressLint(\"PrivateApi\")\n    public static void after() {\n        Hookers.logD(\"ZygoteInit#handleSystemServerProcess() starts\");\n        try {\n            if (systemServerCL == null) {\n                // get system_server classLoader\n                systemServerCL = Thread.currentThread().getContextClassLoader();\n            }\n            // deopt methods in SYSTEMSERVERCLASSPATH\n            PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);\n            var clazz = Class.forName(\"com.android.server.SystemServer\", false, systemServerCL);\n            LSPosedHelper.hookAllMethods(StartBootstrapServicesHooker.class, clazz, \"startBootstrapServices\");\n            if (callback != null) callback.onSystemServerLoaded(systemServerCL);\n        } catch (Throwable t) {\n            Hookers.logE(\"error when hooking systemMain\", t);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCreateCLHooker.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.hooker;\n\nimport static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient;\n\nimport android.annotation.SuppressLint;\nimport android.app.ActivityThread;\nimport android.app.LoadedApk;\nimport android.content.pm.ApplicationInfo;\nimport android.os.Build;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.lspd.impl.LSPosedContext;\nimport org.lsposed.lspd.util.Hookers;\nimport org.lsposed.lspd.util.MetaDataReader;\nimport org.lsposed.lspd.util.Utils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport de.robv.android.xposed.XC_MethodHook;\nimport de.robv.android.xposed.XC_MethodReplacement;\nimport de.robv.android.xposed.XposedBridge;\nimport de.robv.android.xposed.XposedHelpers;\nimport de.robv.android.xposed.XposedInit;\nimport de.robv.android.xposed.callbacks.XC_LoadPackage;\nimport io.github.libxposed.api.XposedInterface;\nimport io.github.libxposed.api.XposedModuleInterface;\n\n@SuppressLint(\"BlockedPrivateApi\")\npublic class LoadedApkCreateCLHooker implements XposedInterface.Hooker {\n    private final static Field defaultClassLoaderField;\n\n    private final static Set<LoadedApk> loadedApks = ConcurrentHashMap.newKeySet();\n\n    static {\n        Field field = null;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            try {\n                field = LoadedApk.class.getDeclaredField(\"mDefaultClassLoader\");\n                field.setAccessible(true);\n            } catch (Throwable ignored) {\n            }\n        }\n        defaultClassLoaderField = field;\n    }\n\n    static void addLoadedApk(LoadedApk loadedApk) {\n        loadedApks.add(loadedApk);\n    }\n\n    public static void after(XposedInterface.AfterHookCallback callback) {\n        LoadedApk loadedApk = (LoadedApk) callback.getThisObject();\n\n        if (callback.getArgs()[0] != null || !loadedApks.contains(loadedApk)) {\n            return;\n        }\n\n        try {\n            Hookers.logD(\"LoadedApk#createClassLoader starts\");\n\n            String packageName = ActivityThread.currentPackageName();\n            String processName = ActivityThread.currentProcessName();\n            boolean isFirstPackage = packageName != null && processName != null && packageName.equals(loadedApk.getPackageName());\n            if (!isFirstPackage) {\n                packageName = loadedApk.getPackageName();\n                processName = ActivityThread.currentPackageName();\n            } else if (packageName.equals(\"android\")) {\n                packageName = \"system\";\n            }\n\n            Object mAppDir = XposedHelpers.getObjectField(loadedApk, \"mAppDir\");\n            ClassLoader classLoader = (ClassLoader) XposedHelpers.getObjectField(loadedApk, \"mClassLoader\");\n            Hookers.logD(\"LoadedApk#createClassLoader ends: \" + mAppDir + \" -> \" + classLoader);\n\n            if (classLoader == null) {\n                return;\n            }\n\n            if (!isFirstPackage && !XposedHelpers.getBooleanField(loadedApk, \"mIncludeCode\")) {\n                Hookers.logD(\"LoadedApk#<init> mIncludeCode == false: \" + mAppDir);\n                return;\n            }\n\n            if (!isFirstPackage && !XposedInit.getLoadedModules().getOrDefault(packageName, Optional.of(\"\")).isPresent()) {\n                return;\n            }\n\n            XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(\n                    XposedBridge.sLoadedPackageCallbacks);\n            lpparam.packageName = packageName;\n            lpparam.processName = processName;\n            lpparam.classLoader = classLoader;\n            lpparam.appInfo = loadedApk.getApplicationInfo();\n            lpparam.isFirstApplication = isFirstPackage;\n\n            if (isFirstPackage && XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty()).isPresent()) {\n                hookNewXSP(lpparam);\n            }\n\n            Hookers.logD(\"Call handleLoadedPackage: packageName=\" + lpparam.packageName + \" processName=\" + lpparam.processName + \" isFirstPackage=\" + isFirstPackage + \" classLoader=\" + lpparam.classLoader + \" appInfo=\" + lpparam.appInfo);\n            XC_LoadPackage.callAll(lpparam);\n\n            LSPosedContext.callOnPackageLoaded(new XposedModuleInterface.PackageLoadedParam() {\n                @NonNull\n                @Override\n                public String getPackageName() {\n                    return loadedApk.getPackageName();\n                }\n\n                @NonNull\n                @Override\n                public ApplicationInfo getApplicationInfo() {\n                    return loadedApk.getApplicationInfo();\n                }\n\n                @NonNull\n                @Override\n                public ClassLoader getDefaultClassLoader() {\n                    try {\n                        return (ClassLoader) defaultClassLoaderField.get(loadedApk);\n                    } catch (Throwable t) {\n                        throw new IllegalStateException(t);\n                    }\n                }\n\n                @NonNull\n                @Override\n                public ClassLoader getClassLoader() {\n                    return classLoader;\n                }\n\n                @Override\n                public boolean isFirstPackage() {\n                    return isFirstPackage;\n                }\n            });\n        } catch (Throwable t) {\n            Hookers.logE(\"error when hooking LoadedApk#createClassLoader\", t);\n        } finally {\n            loadedApks.remove(loadedApk);\n        }\n    }\n\n    private static void hookNewXSP(XC_LoadPackage.LoadPackageParam lpparam) {\n        int xposedminversion = -1;\n        boolean xposedsharedprefs = false;\n        try {\n            Map<String, Object> metaData = MetaDataReader.getMetaData(new File(lpparam.appInfo.sourceDir));\n            Object minVersionRaw = metaData.get(\"xposedminversion\");\n            if (minVersionRaw instanceof Integer) {\n                xposedminversion = (Integer) minVersionRaw;\n            } else if (minVersionRaw instanceof String) {\n                xposedminversion = MetaDataReader.extractIntPart((String) minVersionRaw);\n            }\n            xposedsharedprefs = metaData.containsKey(\"xposedsharedprefs\");\n        } catch (NumberFormatException | IOException e) {\n            Hookers.logE(\"ApkParser fails\", e);\n        }\n\n        if (xposedminversion > 92 || xposedsharedprefs) {\n            Utils.logI(\"New modules detected, hook preferences\");\n            XposedHelpers.findAndHookMethod(\"android.app.ContextImpl\", lpparam.classLoader, \"checkMode\", int.class, new XC_MethodHook() {\n                @Override\n                protected void afterHookedMethod(MethodHookParam param) {\n                    if (((int) param.args[0] & 1/*Context.MODE_WORLD_READABLE*/) != 0) {\n                        param.setThrowable(null);\n                    }\n                }\n            });\n            XposedHelpers.findAndHookMethod(\"android.app.ContextImpl\", lpparam.classLoader, \"getPreferencesDir\", new XC_MethodReplacement() {\n                @Override\n                protected Object replaceHookedMethod(MethodHookParam param) {\n                    return new File(serviceClient.getPrefsPath(lpparam.packageName));\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.hooker;\n\nimport android.app.LoadedApk;\nimport android.content.res.XResources;\n\nimport org.lsposed.lspd.util.Hookers;\nimport org.lsposed.lspd.util.Utils.Log;\n\nimport de.robv.android.xposed.XposedHelpers;\nimport de.robv.android.xposed.XposedInit;\nimport io.github.libxposed.api.XposedInterface;\n\n// when a package is loaded for an existing process, trigger the callbacks as well\npublic class LoadedApkCtorHooker implements XposedInterface.Hooker {\n\n    public static void after(XposedInterface.AfterHookCallback callback) {\n        Hookers.logD(\"LoadedApk#<init> starts\");\n\n        try {\n            LoadedApk loadedApk = (LoadedApk) callback.getThisObject();\n            assert loadedApk != null;\n            String packageName = loadedApk.getPackageName();\n            Object mAppDir = XposedHelpers.getObjectField(loadedApk, \"mAppDir\");\n            Hookers.logD(\"LoadedApk#<init> ends: \" + mAppDir);\n\n            if (!XposedInit.disableResources) {\n                XResources.setPackageNameForResDir(packageName, loadedApk.getResDir());\n            }\n\n            if (packageName.equals(\"android\")) {\n                if (XposedInit.startsSystemServer) {\n                    Hookers.logD(\"LoadedApk#<init> is android, skip: \" + mAppDir);\n                    return;\n                } else {\n                    packageName = \"system\";\n                }\n            }\n\n            if (!XposedInit.loadedPackagesInProcess.add(packageName)) {\n                Hookers.logD(\"LoadedApk#<init> has been loaded before, skip: \" + mAppDir);\n                return;\n            }\n\n            // OnePlus magic...\n            if (Log.getStackTraceString(new Throwable()).\n                    contains(\"android.app.ActivityThread$ApplicationThread.schedulePreload\")) {\n                Hookers.logD(\"LoadedApk#<init> maybe oneplus's custom opt, skip\");\n                return;\n            }\n\n            LoadedApkCreateCLHooker.addLoadedApk(loadedApk);\n        } catch (Throwable t) {\n            Hookers.logE(\"error when hooking LoadedApk.<init>\", t);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/OpenDexFileHooker.java",
    "content": "package org.lsposed.lspd.hooker;\n\nimport android.os.Build;\n\nimport org.lsposed.lspd.impl.LSPosedBridge;\nimport org.matrix.vector.nativebridge.HookBridge;\n\nimport io.github.libxposed.api.XposedInterface;\n\npublic class OpenDexFileHooker implements XposedInterface.Hooker {\n\n    public static void after(XposedInterface.AfterHookCallback callback) {\n        ClassLoader classLoader = null;\n        for (var arg : callback.getArgs()) {\n            if (arg instanceof ClassLoader) {\n                classLoader = (ClassLoader) arg;\n            }\n        }\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P && classLoader == null) {\n            classLoader = LSPosedBridge.class.getClassLoader();\n        }\n        while (classLoader != null) {\n            if (classLoader == LSPosedBridge.class.getClassLoader()) {\n                HookBridge.setTrusted(callback.getResult());\n                return;\n            } else {\n                classLoader = classLoader.getParent();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/hooker/StartBootstrapServicesHooker.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.hooker;\n\nimport static org.lsposed.lspd.util.Utils.logD;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.lspd.impl.LSPosedContext;\nimport org.lsposed.lspd.util.Hookers;\n\nimport de.robv.android.xposed.XposedBridge;\nimport de.robv.android.xposed.XposedInit;\nimport de.robv.android.xposed.callbacks.XC_LoadPackage;\nimport io.github.libxposed.api.XposedInterface;\nimport io.github.libxposed.api.XposedModuleInterface;\n\npublic class StartBootstrapServicesHooker implements XposedInterface.Hooker {\n\n    public static void before() {\n        logD(\"SystemServer#startBootstrapServices() starts\");\n\n        try {\n            XposedInit.loadedPackagesInProcess.add(\"android\");\n\n            XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);\n            lpparam.packageName = \"android\";\n            lpparam.processName = \"android\"; // it's actually system_server, but other functions return this as well\n            lpparam.classLoader = HandleSystemServerProcessHooker.systemServerCL;\n            lpparam.appInfo = null;\n            lpparam.isFirstApplication = true;\n            XC_LoadPackage.callAll(lpparam);\n\n            LSPosedContext.callOnSystemServerLoaded(new XposedModuleInterface.SystemServerLoadedParam() {\n                @Override\n                @NonNull\n                public ClassLoader getClassLoader() {\n                    return HandleSystemServerProcessHooker.systemServerCL;\n                }\n            });\n        } catch (Throwable t) {\n            Hookers.logE(\"error when hooking startBootstrapServices\", t);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/impl/LSPosedBridge.java",
    "content": "package org.lsposed.lspd.impl;\n\nimport androidx.annotation.NonNull;\n\nimport org.matrix.vector.nativebridge.HookBridge;\nimport org.lsposed.lspd.util.Utils.Log;\n\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\n\nimport de.robv.android.xposed.XposedBridge;\nimport io.github.libxposed.api.XposedInterface;\nimport io.github.libxposed.api.errors.HookFailedError;\n\npublic class LSPosedBridge {\n\n    private static final String TAG = \"LSPosed-Bridge\";\n\n    private static final String castException = \"Return value's type from hook callback does not match the hooked method\";\n\n    private static final Method getCause;\n\n    static {\n        Method tmp;\n        try {\n            tmp = InvocationTargetException.class.getMethod(\"getCause\");\n        } catch (Throwable e) {\n            tmp = null;\n        }\n        getCause = tmp;\n    }\n\n    public static class HookerCallback {\n        @NonNull\n        final Method beforeInvocation;\n        @NonNull\n        final Method afterInvocation;\n\n        final int beforeParams;\n        final int afterParams;\n\n        public HookerCallback(@NonNull Method beforeInvocation, @NonNull Method afterInvocation) {\n            this.beforeInvocation = beforeInvocation;\n            this.afterInvocation = afterInvocation;\n            this.beforeParams = beforeInvocation.getParameterCount();\n            this.afterParams = afterInvocation.getParameterCount();\n        }\n    }\n\n    public static void log(String text) {\n        Log.i(TAG, text);\n    }\n\n    public static void log(Throwable t) {\n        String logStr = Log.getStackTraceString(t);\n        Log.e(TAG, logStr);\n    }\n\n    public static class NativeHooker<T extends Executable> {\n        private final Object params;\n\n        private NativeHooker(Executable method) {\n            var isStatic = Modifier.isStatic(method.getModifiers());\n            Object returnType;\n            if (method instanceof Method) {\n                returnType = ((Method) method).getReturnType();\n            } else {\n                returnType = null;\n            }\n            params = new Object[]{\n                    method,\n                    returnType,\n                    isStatic,\n            };\n        }\n\n        // This method is quite critical. We should try not to use system methods to avoid\n        // endless recursive\n        public Object callback(Object[] args) throws Throwable {\n            LSPosedHookCallback<T> callback = new LSPosedHookCallback<>();\n\n            var array = ((Object[]) params);\n\n            var method = (T) array[0];\n            var returnType = (Class<?>) array[1];\n            var isStatic = (Boolean) array[2];\n\n            callback.method = method;\n\n            if (isStatic) {\n                callback.thisObject = null;\n                callback.args = args;\n            } else {\n                callback.thisObject = args[0];\n                callback.args = new Object[args.length - 1];\n                //noinspection ManualArrayCopy\n                for (int i = 0; i < args.length - 1; ++i) {\n                    callback.args[i] = args[i + 1];\n                }\n            }\n\n            Object[][] callbacksSnapshot = HookBridge.callbackSnapshot(HookerCallback.class, method);\n            Object[] modernSnapshot = callbacksSnapshot[0];\n            Object[] legacySnapshot = callbacksSnapshot[1];\n\n            if (modernSnapshot.length == 0 && legacySnapshot.length == 0) {\n                try {\n                    return HookBridge.invokeOriginalMethod(method, callback.thisObject, callback.args);\n                } catch (InvocationTargetException ite) {\n                    throw (Throwable) HookBridge.invokeOriginalMethod(getCause, ite);\n                }\n            }\n\n            Object[] ctxArray = new Object[modernSnapshot.length];\n            XposedBridge.LegacyApiSupport<T> legacy = null;\n\n            // call \"before method\" callbacks\n            int beforeIdx;\n            for (beforeIdx = 0; beforeIdx < modernSnapshot.length; beforeIdx++) {\n                try {\n                    var hooker = (HookerCallback) modernSnapshot[beforeIdx];\n                    if (hooker.beforeParams == 0) {\n                        ctxArray[beforeIdx] = hooker.beforeInvocation.invoke(null);\n                    } else {\n                        ctxArray[beforeIdx] = hooker.beforeInvocation.invoke(null, callback);\n                    }\n                } catch (Throwable t) {\n                    LSPosedBridge.log(t);\n\n                    // reset result (ignoring what the unexpectedly exiting callback did)\n                    callback.setResult(null);\n                    callback.isSkipped = false;\n                    continue;\n                }\n\n                if (callback.isSkipped) {\n                    // skip remaining \"before\" callbacks and corresponding \"after\" callbacks\n                    beforeIdx++;\n                    break;\n                }\n            }\n\n            if (!callback.isSkipped && legacySnapshot.length != 0) {\n                // TODO: Separate classloader\n                legacy = new XposedBridge.LegacyApiSupport<>(callback, legacySnapshot);\n                legacy.handleBefore();\n            }\n\n            // call original method if not requested otherwise\n            if (!callback.isSkipped) {\n                try {\n                    var result = HookBridge.invokeOriginalMethod(method, callback.thisObject, callback.args);\n                    callback.setResult(result);\n                } catch (InvocationTargetException e) {\n                    var throwable = (Throwable) HookBridge.invokeOriginalMethod(getCause, e);\n                    callback.setThrowable(throwable);\n                }\n            }\n\n            // call \"after method\" callbacks\n            for (int afterIdx = beforeIdx - 1; afterIdx >= 0; afterIdx--) {\n                Object lastResult = callback.getResult();\n                Throwable lastThrowable = callback.getThrowable();\n                var hooker = (HookerCallback) modernSnapshot[afterIdx];\n                try {\n                    if (hooker.afterParams == 0) {\n                        hooker.afterInvocation.invoke(null);\n                    } else if (hooker.afterParams == 1) {\n                        hooker.afterInvocation.invoke(null, callback);\n                    } else {\n                        hooker.afterInvocation.invoke(null, callback, ctxArray[afterIdx]);\n                    }\n                } catch (Throwable t) {\n                    LSPosedBridge.log(t);\n\n                    // reset to last result (ignoring what the unexpectedly exiting callback did)\n                    if (lastThrowable == null) {\n                        callback.setResult(lastResult);\n                    } else {\n                        callback.setThrowable(lastThrowable);\n                    }\n                }\n            }\n\n            if (legacy != null) {\n                legacy.handleAfter();\n            }\n\n            // return\n            var t = callback.getThrowable();\n            if (t != null) {\n                throw t;\n            } else {\n                var result = callback.getResult();\n                if (returnType != null && !returnType.isPrimitive() && !HookBridge.instanceOf(result, returnType)) {\n                    throw new ClassCastException(castException);\n                }\n                return result;\n            }\n        }\n    }\n\n    public static void dummyCallback() {\n    }\n\n    public static <T extends Executable> XposedInterface.MethodUnhooker<T>\n    doHook(T hookMethod, int priority, Class<? extends XposedInterface.Hooker> hooker) {\n        if (Modifier.isAbstract(hookMethod.getModifiers())) {\n            throw new IllegalArgumentException(\"Cannot hook abstract methods: \" + hookMethod);\n        } else if (hookMethod.getDeclaringClass().getClassLoader() == LSPosedContext.class.getClassLoader()) {\n            throw new IllegalArgumentException(\"Do not allow hooking inner methods\");\n        } else if (hookMethod.getDeclaringClass() == Method.class && hookMethod.getName().equals(\"invoke\")) {\n            throw new IllegalArgumentException(\"Cannot hook Method.invoke\");\n        } else if (hooker == null) {\n            throw new IllegalArgumentException(\"hooker should not be null!\");\n        }\n\n        Method beforeInvocation = null, afterInvocation = null;\n        var modifiers = Modifier.PUBLIC | Modifier.STATIC;\n        for (var method : hooker.getDeclaredMethods()) {\n            if (method.getName().equals(\"before\")) {\n                if (beforeInvocation != null) {\n                    throw new IllegalArgumentException(\"More than one method named before\");\n                }\n                boolean valid = (method.getModifiers() & modifiers) == modifiers;\n                var params = method.getParameterTypes();\n                if (params.length == 1) {\n                    valid &= params[0].equals(XposedInterface.BeforeHookCallback.class);\n                } else if (params.length != 0) {\n                    valid = false;\n                }\n                if (!valid) {\n                    throw new IllegalArgumentException(\"before method format is invalid\");\n                }\n                beforeInvocation = method;\n            } else if (method.getName().equals(\"after\")) {\n                if (afterInvocation != null) {\n                    throw new IllegalArgumentException(\"More than one method named after\");\n                }\n                boolean valid = (method.getModifiers() & modifiers) == modifiers;\n                valid &= method.getReturnType().equals(void.class);\n                var params = method.getParameterTypes();\n                if (params.length == 1 || params.length == 2) {\n                    valid &= params[0].equals(XposedInterface.AfterHookCallback.class);\n                } else if (params.length != 0) {\n                    valid = false;\n                }\n                if (!valid) {\n                    throw new IllegalArgumentException(\"after method format is invalid\");\n                }\n                afterInvocation = method;\n            }\n        }\n        if (beforeInvocation == null && afterInvocation == null) {\n            throw new IllegalArgumentException(\"No method named before or after found in \" + hooker.getName());\n        }\n        try {\n            if (beforeInvocation == null) {\n                beforeInvocation = LSPosedBridge.class.getMethod(\"dummyCallback\");\n            } else if (afterInvocation == null) {\n                afterInvocation = LSPosedBridge.class.getMethod(\"dummyCallback\");\n            } else {\n                var ret = beforeInvocation.getReturnType();\n                var params = afterInvocation.getParameterTypes();\n                if (ret != void.class && params.length == 2 && !ret.equals(params[1])) {\n                    throw new IllegalArgumentException(\"before and after method format is invalid\");\n                }\n            }\n        } catch (NoSuchMethodException e) {\n            throw new HookFailedError(e);\n        }\n\n        var callback = new LSPosedBridge.HookerCallback(beforeInvocation, afterInvocation);\n        if (HookBridge.hookMethod(true, hookMethod, LSPosedBridge.NativeHooker.class, priority, callback)) {\n            return new XposedInterface.MethodUnhooker<>() {\n                @NonNull\n                @Override\n                public T getOrigin() {\n                    return hookMethod;\n                }\n\n                @Override\n                public void unhook() {\n                    HookBridge.unhookMethod(true, hookMethod, callback);\n                }\n            };\n        }\n        throw new HookFailedError(\"Cannot hook \" + hookMethod);\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java",
    "content": "package org.lsposed.lspd.impl;\n\nimport android.annotation.SuppressLint;\nimport android.app.ActivityThread;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.os.Build;\nimport android.os.ParcelFileDescriptor;\nimport android.os.Process;\nimport android.os.RemoteException;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.lsposed.lspd.core.BuildConfig;\nimport org.lsposed.lspd.models.Module;\nimport org.matrix.vector.nativebridge.HookBridge;\nimport org.matrix.vector.nativebridge.NativeAPI;\nimport org.lsposed.lspd.service.ILSPInjectedModuleService;\nimport org.lsposed.lspd.util.LspModuleClassLoader;\nimport org.lsposed.lspd.util.Utils.Log;\n\nimport org.matrix.vector.impl.utils.VectorDexParser;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\nimport java.nio.ByteBuffer;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport io.github.libxposed.api.XposedInterface;\nimport io.github.libxposed.api.XposedModule;\nimport io.github.libxposed.api.XposedModuleInterface;\nimport io.github.libxposed.api.errors.XposedFrameworkError;\nimport io.github.libxposed.api.utils.DexParser;\n\n\n@SuppressLint(\"NewApi\")\npublic class LSPosedContext implements XposedInterface {\n\n    private static final String TAG = \"LSPosedContext\";\n\n    public static boolean isSystemServer;\n    public static String appDir;\n    public static String processName;\n\n    static final Set<XposedModule> modules = ConcurrentHashMap.newKeySet();\n\n    private final String mPackageName;\n    private final ApplicationInfo mApplicationInfo;\n    private final ILSPInjectedModuleService service;\n    private final Map<String, SharedPreferences> mRemotePrefs = new ConcurrentHashMap<>();\n\n    LSPosedContext(String packageName, ApplicationInfo applicationInfo, ILSPInjectedModuleService service) {\n        this.mPackageName = packageName;\n        this.mApplicationInfo = applicationInfo;\n        this.service = service;\n    }\n\n    public static void callOnPackageLoaded(XposedModuleInterface.PackageLoadedParam param) {\n        for (XposedModule module : modules) {\n            try {\n                module.onPackageLoaded(param);\n            } catch (Throwable t) {\n                Log.e(TAG, \"Error when calling onPackageLoaded of \" + module.getApplicationInfo().packageName, t);\n            }\n        }\n    }\n\n    public static void callOnSystemServerLoaded(XposedModuleInterface.SystemServerLoadedParam param) {\n        for (XposedModule module : modules) {\n            try {\n                module.onSystemServerLoaded(param);\n            } catch (Throwable t) {\n                Log.e(TAG, \"Error when calling onSystemServerLoaded of \" + module.getApplicationInfo().packageName, t);\n            }\n        }\n    }\n\n    @SuppressLint(\"DiscouragedPrivateApi\")\n    public static boolean loadModule(ActivityThread at, Module module) {\n        try {\n            Log.d(TAG, \"Loading module \" + module.packageName);\n            var sb = new StringBuilder();\n            var abis = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS;\n            for (String abi : abis) {\n                sb.append(module.apkPath).append(\"!/lib/\").append(abi).append(File.pathSeparator);\n            }\n            var librarySearchPath = sb.toString();\n            var initLoader = XposedModule.class.getClassLoader();\n            var mcl = LspModuleClassLoader.loadApk(module.apkPath, module.file.preLoadedDexes, librarySearchPath, initLoader);\n            if (mcl.loadClass(XposedModule.class.getName()).getClassLoader() != initLoader) {\n                Log.e(TAG, \"  Cannot load module: \" + module.packageName);\n                Log.e(TAG, \"  The Xposed API classes are compiled into the module's APK.\");\n                Log.e(TAG, \"  This may cause strange issues and must be fixed by the module developer.\");\n                return false;\n            }\n            var ctx = new LSPosedContext(module.packageName, module.applicationInfo, module.service);\n            for (var entry : module.file.moduleClassNames) {\n                var moduleClass = mcl.loadClass(entry);\n                Log.d(TAG, \"  Loading class \" + moduleClass);\n                if (!XposedModule.class.isAssignableFrom(moduleClass)) {\n                    Log.e(TAG, \"    This class doesn't implement any sub-interface of XposedModule, skipping it\");\n                    continue;\n                }\n                try {\n                    var moduleEntry = moduleClass.getConstructor(XposedInterface.class, XposedModuleInterface.ModuleLoadedParam.class);\n                    var moduleContext = (XposedModule) moduleEntry.newInstance(ctx, new XposedModuleInterface.ModuleLoadedParam() {\n                        @Override\n                        public boolean isSystemServer() {\n                            return isSystemServer;\n                        }\n\n                        @NonNull\n                        @Override\n                        public String getProcessName() {\n                            return processName;\n                        }\n                    });\n                    modules.add(moduleContext);\n                } catch (Throwable e) {\n                    Log.e(TAG, \"    Failed to load class \" + moduleClass, e);\n                }\n            }\n            module.file.moduleLibraryNames.forEach(NativeAPI::recordNativeEntrypoint);\n            Log.d(TAG, \"Loaded module \" + module.packageName + \": \" + ctx);\n        } catch (Throwable e) {\n            Log.d(TAG, \"Loading module \" + module.packageName, e);\n            return false;\n        }\n        return true;\n    }\n\n    @NonNull\n    @Override\n    public String getFrameworkName() {\n        return BuildConfig.FRAMEWORK_NAME;\n    }\n\n    @NonNull\n    @Override\n    public String getFrameworkVersion() {\n        return BuildConfig.VERSION_NAME;\n    }\n\n    @Override\n    public long getFrameworkVersionCode() {\n        return BuildConfig.VERSION_CODE;\n    }\n\n    @Override\n    public int getFrameworkPrivilege() {\n        try {\n            return service.getFrameworkPrivilege();\n        } catch (RemoteException ignored) {\n            return -1;\n        }\n    }\n\n    @Override\n    @NonNull\n    public MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker) {\n        return LSPosedBridge.doHook(origin, PRIORITY_DEFAULT, hooker);\n    }\n\n    @Override\n    @NonNull\n    public MethodUnhooker<Method> hook(@NonNull Method origin, int priority, @NonNull Class<? extends Hooker> hooker) {\n        return LSPosedBridge.doHook(origin, priority, hooker);\n    }\n\n    @Override\n    @NonNull\n    public <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Class<? extends Hooker> hooker) {\n        return LSPosedBridge.doHook(origin, PRIORITY_DEFAULT, hooker);\n    }\n\n    @Override\n    @NonNull\n    public <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {\n        return LSPosedBridge.doHook(origin, priority, hooker);\n    }\n\n    @Override\n    @NonNull\n    public <T> MethodUnhooker<Constructor<T>> hookClassInitializer(@NonNull Class<T> origin, @NonNull Class<? extends Hooker> hooker) {\n        return hookClassInitializer(origin, PRIORITY_DEFAULT, hooker);\n    }\n\n    @Override\n    @NonNull\n    @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n    public <T> MethodUnhooker<Constructor<T>> hookClassInitializer(@NonNull Class<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {\n        Method staticInitializer = HookBridge.getStaticInitializer(origin);\n\n        // The class might not have a static initializer block\n        if (staticInitializer == null) {\n            throw new IllegalArgumentException(\"Class \" + origin.getName() + \" has no static initializer\");\n        }\n\n        // Use the existing doHook logic. It will return a MethodUnhooker<Method>.\n        return (MethodUnhooker) LSPosedBridge.doHook(staticInitializer, priority, hooker);\n    }\n\n    private static boolean doDeoptimize(@NonNull Executable method) {\n        if (Modifier.isAbstract(method.getModifiers())) {\n            throw new IllegalArgumentException(\"Cannot deoptimize abstract methods: \" + method);\n        } else if (Proxy.isProxyClass(method.getDeclaringClass())) {\n            throw new IllegalArgumentException(\"Cannot deoptimize methods from proxy class: \" + method);\n        }\n        return HookBridge.deoptimizeMethod(method);\n    }\n\n    @Override\n    public boolean deoptimize(@NonNull Method method) {\n        return doDeoptimize(method);\n    }\n\n    @Override\n    public <T> boolean deoptimize(@NonNull Constructor<T> constructor) {\n        return doDeoptimize(constructor);\n    }\n\n    @Nullable\n    @Override\n    public Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {\n        return HookBridge.invokeOriginalMethod(method, thisObject, args);\n    }\n\n    @Override\n    public <T> void invokeOrigin(@NonNull Constructor<T> constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {\n        // The bridge returns an Object (null for void/constructors), which we discard.\n        HookBridge.invokeOriginalMethod(constructor, thisObject, args);\n    }\n\n    private static char getTypeShorty(Class<?> type) {\n        if (type == int.class) {\n            return 'I';\n        } else if (type == long.class) {\n            return 'J';\n        } else if (type == float.class) {\n            return 'F';\n        } else if (type == double.class) {\n            return 'D';\n        } else if (type == boolean.class) {\n            return 'Z';\n        } else if (type == byte.class) {\n            return 'B';\n        } else if (type == char.class) {\n            return 'C';\n        } else if (type == short.class) {\n            return 'S';\n        } else if (type == void.class) {\n            return 'V';\n        } else {\n            return 'L';\n        }\n    }\n\n    private static char[] getExecutableShorty(Executable executable) {\n        var parameterTypes = executable.getParameterTypes();\n        var shorty = new char[parameterTypes.length + 1];\n        shorty[0] = getTypeShorty(executable instanceof Method ? ((Method) executable).getReturnType() : void.class);\n        for (int i = 1; i < shorty.length; i++) {\n            shorty[i] = getTypeShorty(parameterTypes[i - 1]);\n        }\n        return shorty;\n    }\n\n    @Nullable\n    @Override\n    public Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {\n        if (Modifier.isStatic(method.getModifiers())) {\n            throw new IllegalArgumentException(\"Cannot invoke special on static method: \" + method);\n        }\n        return HookBridge.invokeSpecialMethod(method, getExecutableShorty(method), method.getDeclaringClass(), thisObject, args);\n    }\n\n    @Override\n    public <T> void invokeSpecial(@NonNull Constructor<T> constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {\n        HookBridge.invokeSpecialMethod(constructor, getExecutableShorty(constructor), constructor.getDeclaringClass(), thisObject, args);\n    }\n\n    @NonNull\n    @Override\n    public <T> T newInstanceOrigin(@NonNull Constructor<T> constructor, Object... args) throws InvocationTargetException, IllegalAccessException, InstantiationException {\n        var obj = HookBridge.allocateObject(constructor.getDeclaringClass());\n        HookBridge.invokeOriginalMethod(constructor, obj, args);\n        return obj;\n    }\n\n    @NonNull\n    @Override\n    public <T, U> U newInstanceSpecial(@NonNull Constructor<T> constructor, @NonNull Class<U> subClass, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {\n        var superClass = constructor.getDeclaringClass();\n        if (!superClass.isAssignableFrom(subClass)) {\n            throw new IllegalArgumentException(subClass + \" is not inherited from \" + superClass);\n        }\n        var obj = HookBridge.allocateObject(subClass);\n        HookBridge.invokeSpecialMethod(constructor, getExecutableShorty(constructor), superClass, obj, args);\n        return obj;\n    }\n\n    @Override\n    public void log(@NonNull String message) {\n        Log.i(TAG, mPackageName + \": \" + message);\n    }\n\n    @Override\n    public void log(@NonNull String message, @NonNull Throwable throwable) {\n        Log.e(TAG, mPackageName + \": \" + message, throwable);\n    }\n\n    @Override\n    public DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {\n        return new VectorDexParser(dexData, includeAnnotations);\n    }\n\n    @NonNull\n    @Override\n    public ApplicationInfo getApplicationInfo() {\n        return mApplicationInfo;\n    }\n\n    @NonNull\n    @Override\n    public SharedPreferences getRemotePreferences(String name) {\n        if (name == null) throw new IllegalArgumentException(\"name must not be null\");\n        return mRemotePrefs.computeIfAbsent(name, n -> {\n            try {\n                return new LSPosedRemotePreferences(service, n);\n            } catch (RemoteException e) {\n                log(\"Failed to get remote preferences\", e);\n                throw new XposedFrameworkError(e);\n            }\n        });\n    }\n\n    @NonNull\n    @Override\n    public String[] listRemoteFiles() {\n        try {\n            return service.getRemoteFileList();\n        } catch (RemoteException e) {\n            log(\"Failed to list remote files\", e);\n            throw new XposedFrameworkError(e);\n        }\n    }\n\n    @NonNull\n    @Override\n    public ParcelFileDescriptor openRemoteFile(String name) throws FileNotFoundException {\n        if (name == null) throw new IllegalArgumentException(\"name must not be null\");\n        try {\n            return service.openRemoteFile(name);\n        } catch (RemoteException e) {\n            throw new FileNotFoundException(e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/impl/LSPosedHelper.java",
    "content": "package org.lsposed.lspd.impl;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport io.github.libxposed.api.XposedInterface;\nimport io.github.libxposed.api.errors.HookFailedError;\n\npublic class LSPosedHelper {\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public static <T> XposedInterface.MethodUnhooker<Method>\n    hookMethod(Class<? extends XposedInterface.Hooker> hooker, Class<T> clazz, String methodName, Class<?>... parameterTypes) {\n        try {\n            var method = clazz.getDeclaredMethod(methodName, parameterTypes);\n            return LSPosedBridge.doHook(method, XposedInterface.PRIORITY_DEFAULT, hooker);\n        } catch (NoSuchMethodException e) {\n            throw new HookFailedError(e);\n        }\n    }\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public static <T> Set<XposedInterface.MethodUnhooker<Method>>\n    hookAllMethods(Class<? extends XposedInterface.Hooker> hooker, Class<T> clazz, String methodName) {\n        var unhooks = new HashSet<XposedInterface.MethodUnhooker<Method>>();\n        for (var method : clazz.getDeclaredMethods()) {\n            if (method.getName().equals(methodName)) {\n                unhooks.add(LSPosedBridge.doHook(method, XposedInterface.PRIORITY_DEFAULT, hooker));\n            }\n        }\n        return unhooks;\n    }\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public static <T> XposedInterface.MethodUnhooker<Constructor<T>>\n    hookConstructor(Class<? extends XposedInterface.Hooker> hooker, Class<T> clazz, Class<?>... parameterTypes) {\n        try {\n            var constructor = clazz.getDeclaredConstructor(parameterTypes);\n            return LSPosedBridge.doHook(constructor, XposedInterface.PRIORITY_DEFAULT, hooker);\n        } catch (NoSuchMethodException e) {\n            throw new HookFailedError(e);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/impl/LSPosedHookCallback.java",
    "content": "package org.lsposed.lspd.impl;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Member;\n\nimport io.github.libxposed.api.XposedInterface;\n\npublic class LSPosedHookCallback<T extends Executable> implements XposedInterface.BeforeHookCallback, XposedInterface.AfterHookCallback {\n\n    public Member method;\n\n    public Object thisObject;\n\n    public Object[] args;\n\n    public Object result;\n\n    public Throwable throwable;\n\n    public boolean isSkipped;\n\n    public LSPosedHookCallback() {\n    }\n\n    // Both before and after\n\n    @NonNull\n    @Override\n    public Member getMember() {\n        return this.method;\n    }\n\n    @Nullable\n    @Override\n    public Object getThisObject() {\n        return this.thisObject;\n    }\n\n    @NonNull\n    @Override\n    public Object[] getArgs() {\n        return this.args;\n    }\n\n    // Before\n\n    @Override\n    public void returnAndSkip(@Nullable Object result) {\n        this.result = result;\n        this.throwable = null;\n        this.isSkipped = true;\n    }\n\n    @Override\n    public void throwAndSkip(@Nullable Throwable throwable) {\n        this.result = null;\n        this.throwable = throwable;\n        this.isSkipped = true;\n    }\n\n    // After\n\n    @Nullable\n    @Override\n    public Object getResult() {\n        return this.result;\n    }\n\n    @Nullable\n    @Override\n    public Throwable getThrowable() {\n        return this.throwable;\n    }\n\n    @Override\n    public boolean isSkipped() {\n        return this.isSkipped;\n    }\n\n    @Override\n    public void setResult(@Nullable Object result) {\n        this.result = result;\n        this.throwable = null;\n    }\n\n    @Override\n    public void setThrowable(@Nullable Throwable throwable) {\n        this.result = null;\n        this.throwable = throwable;\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/impl/LSPosedRemotePreferences.java",
    "content": "package org.lsposed.lspd.impl;\n\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.os.RemoteException;\nimport android.util.ArraySet;\n\nimport androidx.annotation.Nullable;\n\nimport org.lsposed.lspd.service.ILSPInjectedModuleService;\nimport org.lsposed.lspd.service.IRemotePreferenceCallback;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@SuppressWarnings(\"unchecked\")\npublic class LSPosedRemotePreferences implements SharedPreferences {\n\n    private final Map<String, Object> mMap = new ConcurrentHashMap<>();\n\n    final HashSet<OnSharedPreferenceChangeListener> mListeners = new HashSet<>();\n\n    IRemotePreferenceCallback callback = new IRemotePreferenceCallback.Stub() {\n        @Override\n        synchronized public void onUpdate(Bundle bundle) {\n            Set<String> changes = new ArraySet<>();\n            if (bundle.containsKey(\"delete\")) {\n                var deletes = (Set<String>) bundle.getSerializable(\"delete\");\n                changes.addAll(deletes);\n                for (var key : deletes) {\n                    mMap.remove(key);\n                }\n            }\n            if (bundle.containsKey(\"put\")) {\n                var puts = (Map<String, Object>) bundle.getSerializable(\"put\");\n                mMap.putAll(puts);\n                changes.addAll(puts.keySet());\n            }\n            synchronized (mListeners) {\n                for (var key : changes) {\n                    mListeners.forEach(listener -> listener.onSharedPreferenceChanged(LSPosedRemotePreferences.this, key));\n                }\n            }\n        }\n    };\n\n    public LSPosedRemotePreferences(ILSPInjectedModuleService service, String group) throws RemoteException {\n        Bundle output = service.requestRemotePreferences(group, callback);\n        if (output.containsKey(\"map\")) {\n            mMap.putAll((Map<String, Object>) output.getSerializable(\"map\"));\n        }\n    }\n\n    @Override\n    public Map<String, ?> getAll() {\n        return new TreeMap<>(mMap);\n    }\n\n    @Nullable\n    @Override\n    public String getString(String key, @Nullable String defValue) {\n        var v = (String) mMap.getOrDefault(key, defValue);\n        if (v != null) return v;\n        return defValue;\n    }\n\n    @Nullable\n    @Override\n    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {\n        var v = (Set<String>) mMap.getOrDefault(key, defValues);\n        if (v != null) return v;\n        return defValues;\n    }\n\n    @Override\n    public int getInt(String key, int defValue) {\n        var v = (Integer) mMap.getOrDefault(key, defValue);\n        if (v != null) return v;\n        return defValue;\n    }\n\n    @Override\n    public long getLong(String key, long defValue) {\n        var v = (Long) mMap.getOrDefault(key, defValue);\n        if (v != null) return v;\n        return defValue;\n    }\n\n    @Override\n    public float getFloat(String key, float defValue) {\n        var v = (Float) mMap.getOrDefault(key, defValue);\n        if (v != null) return v;\n        return defValue;\n    }\n\n    @Override\n    public boolean getBoolean(String key, boolean defValue) {\n        var v = (Boolean) mMap.getOrDefault(key, defValue);\n        if (v != null) return v;\n        return defValue;\n    }\n\n    @Override\n    public boolean contains(String key) {\n        return mMap.containsKey(key);\n    }\n\n    @Override\n    public Editor edit() {\n        throw new UnsupportedOperationException(\"Read only implementation\");\n    }\n\n    @Override\n    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        synchronized (mListeners) {\n            mListeners.add(listener);\n        }\n    }\n\n    @Override\n    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        synchronized (mListeners) {\n            mListeners.remove(listener);\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/util/ClassPathURLStreamHandler.java",
    "content": "package org.lsposed.lspd.util;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.JarURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.jar.JarFile;\nimport java.util.zip.ZipEntry;\n\nimport sun.net.www.ParseUtil;\nimport sun.net.www.protocol.jar.Handler;\n\nfinal class ClassPathURLStreamHandler extends Handler {\n    private final String fileUri;\n    private final JarFile jarFile;\n\n    ClassPathURLStreamHandler(String jarFileName) throws IOException {\n        jarFile = new JarFile(jarFileName);\n        fileUri = new File(jarFileName).toURI().toString();\n    }\n\n    URL getEntryUrlOrNull(String entryName) {\n        if (jarFile.getEntry(entryName) != null) {\n            try {\n                String encodedName = ParseUtil.encodePath(entryName, false);\n                return new URL(\"jar\", null, -1, fileUri + \"!/\" + encodedName, this);\n            } catch (MalformedURLException e) {\n                throw new RuntimeException(\"Invalid entry name\", e);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    protected URLConnection openConnection(URL url) throws IOException {\n        return new ClassPathURLConnection(url);\n    }\n\n    @Override\n    protected void finalize() throws IOException {\n        jarFile.close();\n    }\n\n    private final class ClassPathURLConnection extends JarURLConnection {\n        private JarFile connectionJarFile = null;\n        private ZipEntry jarEntry = null;\n        private InputStream jarInput = null;\n        private boolean closed = false;\n\n        private ClassPathURLConnection(URL url) throws MalformedURLException {\n            super(url);\n            setUseCaches(false);\n        }\n\n        @Override\n        public void setUseCaches(boolean usecaches) {\n            super.setUseCaches(false);\n        }\n\n        @Override\n        public void connect() throws IOException {\n            if (closed) {\n                throw new IllegalStateException(\"JarURLConnection has been closed\");\n            }\n            if (!connected) {\n                jarEntry = jarFile.getEntry(getEntryName());\n                if (jarEntry == null) {\n                    throw new FileNotFoundException(\"URL=\" + url + \", zipfile=\" + jarFile.getName());\n                }\n                connected = true;\n            }\n        }\n\n        @Override\n        public JarFile getJarFile() throws IOException {\n            connect();\n            if (connectionJarFile != null) return connectionJarFile;\n            return connectionJarFile = new JarFile(jarFile.getName());\n        }\n\n        @Override\n        public InputStream getInputStream() throws IOException {\n            connect();\n            if (jarInput != null) return jarInput;\n            return jarInput = new FilterInputStream(jarFile.getInputStream(jarEntry)) {\n                @Override\n                public void close() throws IOException {\n                    super.close();\n                    closed = true;\n                    jarFile.close();\n                    if (connectionJarFile != null) connectionJarFile.close();\n                }\n            };\n        }\n\n        @Override\n        public String getContentType() {\n            String cType = guessContentTypeFromName(getEntryName());\n            if (cType == null) {\n                cType = \"content/unknown\";\n            }\n            return cType;\n        }\n\n        @Override\n        public int getContentLength() {\n            try {\n                connect();\n                return (int) getJarEntry().getSize();\n            } catch (IOException ignored) {\n            }\n            return -1;\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/util/Hookers.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.util;\n\nimport android.app.ActivityThread;\n\npublic class Hookers {\n\n    public static void logD(String prefix) {\n        Utils.logD(String.format(\"%s: pkg=%s, prc=%s\", prefix, ActivityThread.currentPackageName(),\n            ActivityThread.currentProcessName()));\n    }\n\n    public static void logE(String prefix, Throwable throwable) {\n        Utils.logE(String.format(\"%s: pkg=%s, prc=%s\", prefix, ActivityThread.currentPackageName(),\n            ActivityThread.currentProcessName()), throwable);\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/util/LspModuleClassLoader.java",
    "content": "package org.lsposed.lspd.util;\n\nimport static de.robv.android.xposed.XposedBridge.TAG;\n\nimport android.os.Build;\nimport android.os.SharedMemory;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.system.OsConstants;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresApi;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.jar.JarFile;\nimport java.util.zip.ZipEntry;\n\nimport org.lsposed.lspd.util.Utils.Log;\n\nimport hidden.ByteBufferDexClassLoader;\nimport sun.misc.CompoundEnumeration;\n\n@SuppressWarnings(\"ConstantConditions\")\npublic final class LspModuleClassLoader extends ByteBufferDexClassLoader {\n    private static final String zipSeparator = \"!/\";\n    private static final List<File> systemNativeLibraryDirs =\n            splitPaths(System.getProperty(\"java.library.path\"));\n    private final String apk;\n    private final List<File> nativeLibraryDirs = new ArrayList<>();\n\n    private static List<File> splitPaths(String searchPath) {\n        var result = new ArrayList<File>();\n        if (searchPath == null) return result;\n        for (var path : searchPath.split(File.pathSeparator)) {\n            result.add(new File(path));\n        }\n        return result;\n    }\n\n    private LspModuleClassLoader(ByteBuffer[] dexBuffers,\n                                 ClassLoader parent,\n                                 String apk) {\n        super(dexBuffers, parent);\n        this.apk = apk;\n    }\n\n    @RequiresApi(Build.VERSION_CODES.Q)\n    private LspModuleClassLoader(ByteBuffer[] dexBuffers,\n                                 String librarySearchPath,\n                                 ClassLoader parent,\n                                 String apk) {\n        super(dexBuffers, librarySearchPath, parent);\n        initNativeLibraryDirs(librarySearchPath);\n        this.apk = apk;\n    }\n\n    private void initNativeLibraryDirs(String librarySearchPath) {\n        nativeLibraryDirs.addAll(splitPaths(librarySearchPath));\n        nativeLibraryDirs.addAll(systemNativeLibraryDirs);\n    }\n\n    @Override\n    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n        var cl = findLoadedClass(name);\n        if (cl != null) {\n            return cl;\n        }\n        try {\n            return Object.class.getClassLoader().loadClass(name);\n        } catch (ClassNotFoundException ignored) {\n        }\n        ClassNotFoundException fromSuper;\n        try {\n            return findClass(name);\n        } catch (ClassNotFoundException ex) {\n            fromSuper = ex;\n        }\n        try {\n            return getParent().loadClass(name);\n        } catch (ClassNotFoundException cnfe) {\n            throw fromSuper;\n        }\n    }\n\n    @Override\n    public String findLibrary(String libraryName) {\n        var fileName = System.mapLibraryName(libraryName);\n        for (var file : nativeLibraryDirs) {\n            var path = file.getPath();\n            if (path.contains(zipSeparator)) {\n                var split = path.split(zipSeparator, 2);\n                try (var jarFile = new JarFile(split[0])) {\n                    var entryName = split[1] + '/' + fileName;\n                    var entry = jarFile.getEntry(entryName);\n                    if (entry != null && entry.getMethod() == ZipEntry.STORED) {\n                        return split[0] + zipSeparator + entryName;\n                    }\n                } catch (IOException e) {\n                    Log.e(TAG, \"Can not open \" + split[0], e);\n                }\n            } else if (file.isDirectory()) {\n                var entryPath = new File(file, fileName).getPath();\n                try {\n                    var fd = Os.open(entryPath, OsConstants.O_RDONLY, 0);\n                    Os.close(fd);\n                    return entryPath;\n                } catch (ErrnoException ignored) {\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public String getLdLibraryPath() {\n        var result = new StringBuilder();\n        for (var directory : nativeLibraryDirs) {\n            if (result.length() > 0) {\n                result.append(':');\n            }\n            result.append(directory);\n        }\n        return result.toString();\n    }\n\n    @Override\n    protected URL findResource(String name) {\n        try {\n            var urlHandler = new ClassPathURLStreamHandler(apk);\n            var url = urlHandler.getEntryUrlOrNull(name);\n            if (url == null) {\n                // noinspection FinalizeCalledExplicitly\n                urlHandler.finalize();\n            }\n            return url;\n        } catch (IOException e) {\n            return null;\n        }\n    }\n\n    @Override\n    protected Enumeration<URL> findResources(String name) {\n        var result = new ArrayList<URL>();\n        var url = findResource(name);\n        if (url != null) result.add(url);\n        return Collections.enumeration(result);\n    }\n\n    @Override\n    public URL getResource(String name) {\n        var resource = Object.class.getClassLoader().getResource(name);\n        if (resource != null) return resource;\n        resource = findResource(name);\n        if (resource != null) return resource;\n        final var cl = getParent();\n        return (cl == null) ? null : cl.getResource(name);\n    }\n\n    @Override\n    public Enumeration<URL> getResources(String name) throws IOException {\n        @SuppressWarnings(\"unchecked\") final var resources = (Enumeration<URL>[]) new Enumeration<?>[]{\n                Object.class.getClassLoader().getResources(name),\n                findResources(name),\n                getParent() == null ? null : getParent().getResources(name)};\n        return new CompoundEnumeration<>(resources);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        if (apk == null) return \"LspModuleClassLoader[instantiating]\";\n        return \"LspModuleClassLoader[module=\" + apk + \", \" + super.toString() + \"]\";\n    }\n\n    public static ClassLoader loadApk(String apk,\n                                      List<SharedMemory> dexes,\n                                      String librarySearchPath,\n                                      ClassLoader parent) {\n        var dexBuffers = dexes.stream().parallel().map(dex -> {\n            try {\n                return dex.mapReadOnly();\n            } catch (ErrnoException e) {\n                Log.w(TAG, \"Can not map \" + dex, e);\n                return null;\n            }\n        }).filter(Objects::nonNull).toArray(ByteBuffer[]::new);\n        LspModuleClassLoader cl;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            cl = new LspModuleClassLoader(dexBuffers, librarySearchPath, parent, apk);\n        } else {\n            cl = new LspModuleClassLoader(dexBuffers, parent, apk);\n            cl.initNativeLibraryDirs(librarySearchPath);\n        }\n        Arrays.stream(dexBuffers).parallel().forEach(SharedMemory::unmap);\n        dexes.stream().parallel().forEach(SharedMemory::close);\n        return cl;\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/lsposed/lspd/util/MetaDataReader.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.jar.JarFile;\n\nimport pxb.android.axml.AxmlReader;\nimport pxb.android.axml.AxmlVisitor;\nimport pxb.android.axml.NodeVisitor;\n\npublic class MetaDataReader {\n    private final HashMap<String, Object> metaData = new HashMap<>();\n\n    public static Map<String, Object> getMetaData(File apk) throws IOException {\n        return new MetaDataReader(apk).metaData;\n    }\n\n    private MetaDataReader(File apk) throws IOException {\n        try (JarFile zip = new JarFile(apk);\n             var is = zip.getInputStream(zip.getEntry(\"AndroidManifest.xml\"))) {\n            var reader = new AxmlReader(getBytesFromInputStream(is));\n            reader.accept(new AxmlVisitor() {\n                @Override\n                public NodeVisitor child(String ns, String name) {\n                    NodeVisitor child = super.child(ns, name);\n                    return new ManifestTagVisitor(child);\n                }\n            });\n        }\n    }\n\n    public static byte[] getBytesFromInputStream(InputStream inputStream) throws IOException {\n        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {\n            byte[] b = new byte[1024];\n            int n;\n            while ((n = inputStream.read(b)) != -1) {\n                bos.write(b, 0, n);\n            }\n            return bos.toByteArray();\n        }\n    }\n\n    private class ManifestTagVisitor extends NodeVisitor {\n        public ManifestTagVisitor(NodeVisitor child) {\n            super(child);\n        }\n\n        @Override\n        public NodeVisitor child(String ns, String name) {\n            NodeVisitor child = super.child(ns, name);\n            if (\"application\".equals(name)) {\n                return new ApplicationTagVisitor(child);\n            }\n            return child;\n        }\n\n        private class ApplicationTagVisitor extends NodeVisitor {\n            public ApplicationTagVisitor(NodeVisitor child) {\n                super(child);\n            }\n\n            @Override\n            public NodeVisitor child(String ns, String name) {\n                NodeVisitor child = super.child(ns, name);\n                if (\"meta-data\".equals(name)) {\n                    return new MetaDataVisitor(child);\n                }\n                return child;\n            }\n        }\n    }\n\n    private class MetaDataVisitor extends NodeVisitor {\n        public String name = null;\n        public Object value = null;\n\n        public MetaDataVisitor(NodeVisitor child) {\n            super(child);\n        }\n\n        @Override\n        public void attr(String ns, String name, int resourceId, int type, Object obj) {\n            if (type == 3 && \"name\".equals(name)) {\n                this.name = (String) obj;\n            }\n            if (\"value\".equals(name)) {\n                value = obj;\n            }\n            super.attr(ns, name, resourceId, type, obj);\n        }\n\n        @Override\n        public void end() {\n            if (name != null && value != null) {\n                metaData.put(name, value);\n            }\n            super.end();\n        }\n    }\n\n    public static int extractIntPart(String str) {\n        int result = 0, length = str.length();\n        for (int offset = 0; offset < length; offset++) {\n            char c = str.charAt(offset);\n            if ('0' <= c && c <= '9')\n                result = result * 10 + (c - '0');\n            else\n                break;\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "crowdin.yml",
    "content": "project_id_env: CROWDIN_PROJECT_ID\napi_token_env: CROWDIN_API_TOKEN\nbase_path: .\nbase_url: 'https://api.crowdin.com'\npull_request_title: '[translation] Update translation from Crowdin'\npreserve_hierarchy: 1\nfiles:\n  - source: /app/src/main/res/values/strings.xml\n    translation: /app/src/main/res/values-%two_letters_code%/%original_file_name%\n    type: android\n    dest: /app/strings.xml\n  - source: /daemon/src/main/res/values/strings.xml\n    translation: /daemon/src/main/res/values-%two_letters_code%/%original_file_name%\n    type: android\n    dest: /daemon/strings.xml\n"
  },
  {
    "path": "daemon/.gitignore",
    "content": "/build\n/.cxx\n"
  },
  {
    "path": "daemon/build.gradle.kts",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\nimport com.android.build.api.dsl.ApplicationExtension\nimport com.android.ide.common.signing.KeystoreHelper\nimport java.io.PrintStream\n\nplugins {\n    alias(libs.plugins.agp.app)\n    alias(libs.plugins.lsplugin.resopt)\n}\n\nval daemonName = \"LSPosed\"\n\nval injectedPackageName: String by rootProject.extra\nval injectedPackageUid: Int by rootProject.extra\n\nval agpVersion: String by project\n\nval defaultManagerPackageName: String by rootProject.extra\n\nandroid {\n    buildFeatures {\n        prefab = true\n        buildConfig = true\n    }\n\n    defaultConfig {\n        applicationId = \"org.lsposed.daemon\"\n\n        buildConfigField(\n            \"String\",\n            \"DEFAULT_MANAGER_PACKAGE_NAME\",\n            \"\"\"\"$defaultManagerPackageName\"\"\"\",\n        )\n        buildConfigField(\"String\", \"MANAGER_INJECTED_PKG_NAME\", \"\"\"\"$injectedPackageName\"\"\"\")\n        buildConfigField(\"int\", \"MANAGER_INJECTED_UID\", \"\"\"$injectedPackageUid\"\"\")\n    }\n\n    buildTypes {\n        all {\n            externalNativeBuild { cmake { arguments += \"-DANDROID_ALLOW_UNDEFINED_SYMBOLS=true\" } }\n        }\n        release {\n            isMinifyEnabled = true\n            isShrinkResources = true\n            proguardFiles(\"proguard-rules.pro\")\n        }\n    }\n\n    externalNativeBuild { cmake { path(\"src/main/jni/CMakeLists.txt\") } }\n\n    namespace = \"org.lsposed.daemon\"\n}\n\nandroid.applicationVariants.all {\n    val variantCapped = name.replaceFirstChar { it.uppercase() }\n    val variantLowered = name.lowercase()\n\n    val outSrcDir = layout.buildDirectory.dir(\"generated/source/signInfo/${variantLowered}\").get()\n    val signInfoTask =\n        tasks.register(\"generate${variantCapped}SignInfo\") {\n            dependsOn(\":app:validateSigning${variantCapped}\")\n            val sign =\n                rootProject\n                    .project(\":app\")\n                    .extensions\n                    .getByType(ApplicationExtension::class.java)\n                    .buildTypes\n                    .named(variantLowered)\n                    .get()\n                    .signingConfig\n            val outSrc = file(\"$outSrcDir/org/lsposed/lspd/util/SignInfo.java\")\n            outputs.file(outSrc)\n            doLast {\n                outSrc.parentFile.mkdirs()\n                val certificateInfo =\n                    KeystoreHelper.getCertificateInfo(\n                        sign?.storeType,\n                        sign?.storeFile,\n                        sign?.storePassword,\n                        sign?.keyPassword,\n                        sign?.keyAlias,\n                    )\n                PrintStream(outSrc)\n                    .print(\n                        \"\"\"\n                |package org.lsposed.lspd.util;\n                |public final class SignInfo {\n                |    public static final byte[] CERTIFICATE = {${\n                    certificateInfo.certificate.encoded.joinToString(\",\")\n                }};\n                |}\"\"\"\n                            .trimMargin()\n                    )\n            }\n        }\n    registerJavaGeneratingTask(signInfoTask, outSrcDir.asFile)\n}\n\ndependencies {\n    implementation(libs.agp.apksig)\n    implementation(projects.external.apache)\n    implementation(projects.hiddenapi.bridge)\n    implementation(projects.services.daemonService)\n    implementation(projects.services.managerService)\n    compileOnly(libs.androidx.annotation)\n    compileOnly(projects.hiddenapi.stubs)\n}\n"
  },
  {
    "path": "daemon/proguard-rules.pro",
    "content": "-keepclasseswithmembers,includedescriptorclasses class * {\n    native <methods>;\n}\n-keepclasseswithmembers class org.lsposed.lspd.Main {\n    public static void main(java.lang.String[]);\n}\n-keepclasseswithmembers class org.lsposed.lspd.service.Dex2OatService {\n    private java.lang.String devTmpDir;\n    private java.lang.String magiskPath;\n    private java.lang.String fakeBin32;\n    private java.lang.String fakeBin64;\n    private java.lang.String[] dex2oatBinaries;\n}\n-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService {\n    private int refreshFd(boolean);\n}\n-keepclassmembers class ** implements android.content.ContextWrapper {\n    public int getUserId();\n    public android.os.UserHandle getUser();\n}\n-assumenosideeffects class android.util.Log {\n    public static *** v(...);\n    public static *** d(...);\n}\n-repackageclasses\n-allowaccessmodification\n"
  },
  {
    "path": "daemon/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/Main.java",
    "content": "package org.lsposed.lspd;\n\nimport org.lsposed.lspd.service.ServiceManager;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        ServiceManager.start(args);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/ActivityManagerService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.app.ContentProviderHolder;\nimport android.app.IActivityManager;\nimport android.app.IApplicationThread;\nimport android.app.IServiceConnection;\nimport android.app.IUidObserver;\nimport android.app.ProfilerInfo;\nimport android.content.Context;\nimport android.content.IContentProvider;\nimport android.content.IIntentReceiver;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.UserInfo;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.os.ServiceManager;\nimport android.util.Log;\n\npublic class ActivityManagerService {\n    private static IActivityManager am = null;\n    private static IBinder binder = null;\n    private static IApplicationThread appThread = null;\n    private static IBinder token = null;\n\n    private static final IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {\n        @Override\n        public void binderDied() {\n            Log.w(TAG, \"am is dead\");\n            binder.unlinkToDeath(this, 0);\n            binder = null;\n            am = null;\n            appThread = null;\n            token = null;\n        }\n    };\n\n    public static IActivityManager getActivityManager() {\n        if (binder == null || am == null) {\n            binder = ServiceManager.getService(Context.ACTIVITY_SERVICE);\n            if (binder == null) return null;\n            try {\n                binder.linkToDeath(deathRecipient, 0);\n                am = IActivityManager.Stub.asInterface(binder);\n                // For oddo Android 9 we cannot set activity controller here...\n                // am.setActivityController(null, false);\n            } catch (RemoteException e) {\n                Log.e(TAG, Log.getStackTraceString(e));\n            }\n        }\n        return am;\n    }\n\n    public static int broadcastIntentWithFeature(String callingFeatureId,\n                                                 Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode,\n                                                 String resultData, Bundle map, String[] requiredPermissions,\n                                                 int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null || appThread == null) return -1;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n            try {\n                return am.broadcastIntentWithFeature(appThread, callingFeatureId, intent, resolvedType, resultTo,\n                        resultCode, resultData, null, requiredPermissions, null, null, appOp, null,\n                        serialized, sticky, userId);\n            } catch (NoSuchMethodError ignored) {\n                return am.broadcastIntentWithFeature(appThread, callingFeatureId, intent, resolvedType, resultTo,\n                        resultCode, resultData, null, requiredPermissions, null, appOp, null,\n                        serialized, sticky, userId);\n            }\n        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n            return am.broadcastIntentWithFeature(appThread, callingFeatureId, intent, resolvedType, resultTo, resultCode, resultData, map, requiredPermissions, appOp, options, serialized, sticky, userId);\n        } else {\n            return am.broadcastIntent(appThread, intent, resolvedType, resultTo, resultCode, resultData, map, requiredPermissions, appOp, options, serialized, sticky, userId);\n        }\n    }\n\n    public static void forceStopPackage(String packageName, int userId) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return;\n        am.forceStopPackage(packageName, userId);\n    }\n\n    public static boolean startUserInBackground(int userId) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return false;\n        return am.startUserInBackground(userId);\n    }\n\n    public static Intent registerReceiver(String callerPackage,\n                                          String callingFeatureId, IIntentReceiver receiver, IntentFilter filter,\n                                          String requiredPermission, int userId, int flags) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null || appThread == null) return null;\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)\n            return am.registerReceiverWithFeature(appThread, callerPackage, callingFeatureId, \"null\", receiver, filter, requiredPermission, userId, flags);\n        else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n            return am.registerReceiverWithFeature(appThread, callerPackage, callingFeatureId, receiver, filter, requiredPermission, userId, flags);\n        } else {\n            return am.registerReceiver(appThread, callerPackage, receiver, filter, requiredPermission, userId, flags);\n        }\n    }\n\n    public static void finishReceiver(IBinder intentReceiver, IBinder applicationThread, int resultCode,\n                                      String resultData, Bundle resultExtras, boolean resultAbort,\n                                      int flags) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null || appThread == null) return;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {\n            am.finishReceiver(applicationThread, resultCode, resultData, resultExtras, resultAbort, flags);\n        } else {\n            am.finishReceiver(intentReceiver, resultCode, resultData, resultExtras, resultAbort, flags);\n        }\n    }\n\n    public static int bindService(Intent service,\n                                  String resolvedType, IServiceConnection connection, int flags,\n                                  String callingPackage, int userId) throws RemoteException {\n\n        IActivityManager am = getActivityManager();\n        if (am == null || appThread == null) return -1;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)\n            return am.bindService(appThread, token, service, resolvedType, connection, (long) flags, callingPackage, userId);\n        else\n            return am.bindService(appThread, token, service, resolvedType, connection, flags, callingPackage, userId);\n    }\n\n    public static boolean unbindService(IServiceConnection connection) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return false;\n        return am.unbindService(connection);\n    }\n\n    public static int startActivityAsUserWithFeature(String callingPackage,\n                                                     String callingFeatureId, Intent intent, String resolvedType,\n                                                     IBinder resultTo, String resultWho, int requestCode, int flags,\n                                                     ProfilerInfo profilerInfo, Bundle options, int userId) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null || appThread == null) return -1;\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            return am.startActivityAsUserWithFeature(appThread, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, flags, profilerInfo, options, userId);\n        } else {\n            return am.startActivityAsUser(appThread, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, flags, profilerInfo, options, userId);\n        }\n    }\n\n    public static void onSystemServerContext(IApplicationThread thread, IBinder token) {\n        ActivityManagerService.appThread = thread;\n        ActivityManagerService.token = token;\n    }\n\n    public static boolean switchUser(int userid) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return false;\n        return am.switchUser(userid);\n    }\n\n    public static UserInfo getCurrentUser() throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return null;\n        return am.getCurrentUser();\n    }\n\n    public static Configuration getConfiguration() throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return null;\n        return am.getConfiguration();\n    }\n\n    public static IContentProvider getContentProvider(String auth, int userId) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return null;\n        ContentProviderHolder holder;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            holder = am.getContentProviderExternal(auth, userId, token, null);\n        } else {\n            holder = am.getContentProviderExternal(auth, userId, token);\n        }\n        return holder != null ? holder.provider : null;\n    }\n\n    public static void registerUidObserver(IUidObserver observer, int which, int cutpoint, String callingPackage) throws RemoteException {\n        IActivityManager am = getActivityManager();\n        if (am == null) return;\n        am.registerUidObserver(observer, which, cutpoint, callingPackage);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/BridgeService.java",
    "content": "package org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.app.ActivityManager;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Looper;\nimport android.os.Parcel;\nimport android.os.ServiceManager;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.util.Log;\n\nimport org.lsposed.daemon.BuildConfig;\n\nimport java.lang.reflect.Field;\nimport java.util.Map;\n\npublic class BridgeService {\n\n    static final int TRANSACTION_CODE = ('_' << 24) | ('V' << 16) | ('E' << 8) | 'C';\n    private static final String SERVICE_NAME = \"activity\";\n\n    enum ACTION {\n        ACTION_UNKNOWN,\n        ACTION_SEND_BINDER,\n        ACTION_GET_BINDER,\n    }\n\n    public interface Listener {\n        void onSystemServerRestarted();\n\n        void onResponseFromBridgeService(boolean response);\n\n        void onSystemServerDied();\n    }\n\n    private static IBinder serviceBinder = null;\n\n    private static Listener listener;\n    private static IBinder bridgeService;\n    private static final IBinder.DeathRecipient bridgeRecipient = new IBinder.DeathRecipient() {\n\n        @Override\n        public void binderDied() {\n            Log.i(TAG, \"service \" + SERVICE_NAME + \" is dead. \");\n\n            try {\n                //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi\n                Field field = ServiceManager.class.getDeclaredField(\"sServiceManager\");\n                field.setAccessible(true);\n                field.set(null, null);\n\n                //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi\n                field = ServiceManager.class.getDeclaredField(\"sCache\");\n                field.setAccessible(true);\n                Object sCache = field.get(null);\n                if (sCache instanceof Map) {\n                    //noinspection rawtypes\n                    ((Map) sCache).clear();\n                }\n                Log.i(TAG, \"clear ServiceManager\");\n\n                //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi\n                field = ActivityManager.class.getDeclaredField(\"IActivityManagerSingleton\");\n                field.setAccessible(true);\n                Object singleton = field.get(null);\n                if (singleton != null) {\n                    //noinspection PrivateApi DiscouragedPrivateApi\n                    field = Class.forName(\"android.util.Singleton\").getDeclaredField(\"mInstance\");\n                    field.setAccessible(true);\n                    synchronized (singleton) {\n                        field.set(singleton, null);\n                    }\n                }\n                Log.i(TAG, \"clear ActivityManager\");\n            } catch (Throwable e) {\n                Log.w(TAG, \"clear ServiceManager: \" + Log.getStackTraceString(e));\n            }\n\n            bridgeService.unlinkToDeath(this, 0);\n            bridgeService = null;\n            listener.onSystemServerDied();\n            new Handler(Looper.getMainLooper()).post(() -> sendToBridge(serviceBinder, true));\n        }\n    };\n\n    // For service\n    // This MUST run in main thread\n    private static synchronized void sendToBridge(IBinder binder, boolean isRestart) {\n        assert Looper.myLooper() == Looper.getMainLooper();\n        try {\n            Os.seteuid(0);\n        } catch (ErrnoException e) {\n            Log.e(TAG, \"seteuid 0\", e);\n        }\n        try {\n            do {\n                bridgeService = ServiceManager.getService(SERVICE_NAME);\n                if (bridgeService != null && bridgeService.pingBinder()) {\n                    break;\n                }\n\n                Log.i(TAG, \"service \" + SERVICE_NAME + \" is not started, wait 1s.\");\n\n                try {\n                    //noinspection BusyWait\n                    Thread.sleep(1000);\n                } catch (Throwable e) {\n                    Log.w(TAG, \"sleep\" + Log.getStackTraceString(e));\n                }\n            } while (true);\n\n            if (isRestart && listener != null) {\n                listener.onSystemServerRestarted();\n            }\n\n            try {\n                bridgeService.linkToDeath(bridgeRecipient, 0);\n            } catch (Throwable e) {\n                Log.w(TAG, \"linkToDeath \" + Log.getStackTraceString(e));\n                var snapshot = bridgeService;\n                sendToBridge(binder, snapshot == null || !snapshot.isBinderAlive());\n                return;\n            }\n\n            boolean res = false;\n            // try at most three times\n            for (int i = 0; i < 3; i++) {\n                Parcel data = Parcel.obtain();\n                Parcel reply = Parcel.obtain();\n                try {\n                    data.writeInt(ACTION.ACTION_SEND_BINDER.ordinal());\n                    Log.v(TAG, \"binder \" + binder.toString());\n                    data.writeStrongBinder(binder);\n                    if (bridgeService == null) break;\n                    res = bridgeService.transact(TRANSACTION_CODE, data, reply, 0);\n                    reply.readException();\n                } catch (Throwable e) {\n                    Log.e(TAG, \"send binder \" + Log.getStackTraceString(e));\n                    var snapshot = bridgeService;\n                    sendToBridge(binder, snapshot == null || !snapshot.isBinderAlive());\n                    return;\n                } finally {\n                    data.recycle();\n                    reply.recycle();\n                }\n\n                if (res) break;\n\n                Log.w(TAG, \"no response from bridge, retry in 1s\");\n\n                try {\n                    Thread.sleep(1000);\n                } catch (InterruptedException ignored) {\n                }\n            }\n\n            if (listener != null) {\n                listener.onResponseFromBridgeService(res);\n            }\n        } finally {\n            try {\n                if (!BuildConfig.DEBUG) {\n                    Os.seteuid(1000);\n                }\n            } catch (ErrnoException e) {\n                Log.e(TAG, \"seteuid 1000\", e);\n            }\n        }\n    }\n\n    public static void send(LSPosedService service, Listener listener) {\n        BridgeService.listener = listener;\n        BridgeService.serviceBinder = service.asBinder();\n        sendToBridge(serviceBinder, false);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 - 2022 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\nimport static org.lsposed.lspd.service.ServiceManager.toGlobalNamespace;\n\nimport android.content.res.AssetManager;\nimport android.content.res.Resources;\nimport android.os.Binder;\nimport android.os.ParcelFileDescriptor;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.os.SELinux;\nimport android.os.SharedMemory;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.system.OsConstants;\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\n\nimport org.lsposed.daemon.BuildConfig;\nimport org.lsposed.lspd.models.PreLoadedApk;\nimport org.lsposed.lspd.util.InstallerVerifier;\nimport org.lsposed.lspd.util.Utils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.lang.reflect.Method;\nimport java.nio.channels.Channels;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.FileLock;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.OpenOption;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.StandardOpenOption;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.nio.file.attribute.PosixFilePermissions;\nimport java.time.Instant;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.stream.Stream;\nimport java.util.zip.Deflater;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipOutputStream;\n\nimport hidden.HiddenApiBridge;\n\npublic class ConfigFileManager {\n    static final Path basePath = Paths.get(\"/data/adb/lspd\");\n    static final Path modulePath = basePath.resolve(\"modules\");\n    static final Path daemonApkPath = Paths.get(System.getProperty(\"java.class.path\", null));\n    static final Path managerApkPath = daemonApkPath.getParent().resolve(\"manager.apk\");\n    static final File magiskDbPath = new File(\"/data/adb/magisk.db\");\n    private static final Path lockPath = basePath.resolve(\"lock\");\n    private static final Path configDirPath = basePath.resolve(\"config\");\n    static final File dbPath = configDirPath.resolve(\"modules_config.db\").toFile();\n    private static final Path logDirPath = basePath.resolve(\"log\");\n    private static final Path oldLogDirPath = basePath.resolve(\"log.old\");\n    private static final DateTimeFormatter formatter =\n            DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(Utils.getZoneId());\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private static FileLocker locker = null;\n    private static Resources res = null;\n    private static ParcelFileDescriptor fd = null;\n    private static SharedMemory preloadDex = null;\n\n    static {\n        try {\n            Files.createDirectories(basePath);\n            SELinux.setFileContext(basePath.toString(), \"u:object_r:system_file:s0\");\n            Files.createDirectories(configDirPath);\n            createLogDirPath();\n        } catch (IOException e) {\n            Log.e(TAG, Log.getStackTraceString(e));\n        }\n    }\n\n    public static void transfer(InputStream in, OutputStream out) throws IOException {\n        int size = 8192;\n        var buffer = new byte[size];\n        int read;\n        while ((read = in.read(buffer, 0, size)) >= 0) {\n            out.write(buffer, 0, read);\n        }\n    }\n\n    private static void createLogDirPath() throws IOException {\n        if (!Files.isDirectory(logDirPath, LinkOption.NOFOLLOW_LINKS)) {\n            Files.deleteIfExists(logDirPath);\n        }\n        Files.createDirectories(logDirPath);\n    }\n\n    public static Resources getResources() {\n        loadRes();\n        return res;\n    }\n\n    private static void loadRes() {\n        if (res != null) return;\n        try {\n            var am = AssetManager.class.newInstance();\n            //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi\n            Method addAssetPath = AssetManager.class.getDeclaredMethod(\"addAssetPath\", String.class);\n            addAssetPath.setAccessible(true);\n            //noinspection ConstantConditions\n            if ((int) addAssetPath.invoke(am, daemonApkPath.toString()) > 0) {\n                //noinspection deprecation\n                res = new Resources(am, null, null);\n            }\n        } catch (Throwable e) {\n            Log.e(TAG, Log.getStackTraceString(e));\n        }\n    }\n\n    static void reloadConfiguration() {\n        loadRes();\n        try {\n            var conf = ActivityManagerService.getConfiguration();\n            if (conf != null)\n                //noinspection deprecation\n                res.updateConfiguration(conf, res.getDisplayMetrics());\n        } catch (Throwable e) {\n            Log.e(TAG, \"reload configuration\", e);\n        }\n    }\n\n    static ParcelFileDescriptor getManagerApk() throws IOException {\n        if (fd != null) return fd.dup();\n        InstallerVerifier.verifyInstallerSignature(managerApkPath.toString());\n\n        SELinux.setFileContext(managerApkPath.toString(), \"u:object_r:system_file:s0\");\n        fd = ParcelFileDescriptor.open(managerApkPath.toFile(), ParcelFileDescriptor.MODE_READ_ONLY);\n        return fd.dup();\n    }\n\n    static void deleteFolderIfExists(Path target) throws IOException {\n        if (Files.notExists(target)) return;\n        Files.walkFileTree(target, new SimpleFileVisitor<>() {\n            @Override\n            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)\n                    throws IOException {\n                Files.delete(file);\n                return FileVisitResult.CONTINUE;\n            }\n\n            @Override\n            public FileVisitResult postVisitDirectory(Path dir, IOException e)\n                    throws IOException {\n                if (e == null) {\n                    Files.delete(dir);\n                    return FileVisitResult.CONTINUE;\n                } else {\n                    throw e;\n                }\n            }\n        });\n    }\n\n    public static boolean chattr0(Path path) {\n        try {\n            var dir = Os.open(path.toString(), OsConstants.O_RDONLY, 0);\n            // Clear all special file attributes on the directory\n            HiddenApiBridge.Os_ioctlInt(dir, Process.is64Bit() ? 0x40086602 : 0x40046602, 0);\n            Os.close(dir);\n            return true;\n        } catch (ErrnoException e) {\n            // If the operation is not supported (ENOTSUP), it means the filesystem doesn't support attributes.\n            // We can assume the file is not immutable and proceed.\n            if (e.errno == OsConstants.ENOTSUP) {\n                return true;\n            }\n            Log.d(TAG, \"chattr 0\", e);\n            return false;\n        } catch (Throwable e) {\n            Log.d(TAG, \"chattr 0\", e);\n            return false;\n        }\n    }\n\n    static void moveLogDir() {\n        try {\n            if (Files.exists(logDirPath)) {\n                if (chattr0(logDirPath)) {\n                    deleteFolderIfExists(oldLogDirPath);\n                    Files.move(logDirPath, oldLogDirPath);\n                }\n            }\n            Files.createDirectories(logDirPath);\n        } catch (IOException e) {\n            Log.e(TAG, Log.getStackTraceString(e));\n        }\n    }\n\n    private static String getNewLogFileName(String prefix) {\n        return prefix + \"_\" + formatter.format(Instant.now()) + \".log\";\n    }\n\n    static File getNewVerboseLogPath() throws IOException {\n        createLogDirPath();\n        return logDirPath.resolve(getNewLogFileName(\"verbose\")).toFile();\n    }\n\n    static File getNewModulesLogPath() throws IOException {\n        createLogDirPath();\n        return logDirPath.resolve(getNewLogFileName(\"modules\")).toFile();\n    }\n\n    static File getPropsPath() throws IOException {\n        createLogDirPath();\n        return logDirPath.resolve(\"props.txt\").toFile();\n    }\n\n    static File getKmsgPath() throws IOException {\n        createLogDirPath();\n        return logDirPath.resolve(\"kmsg.log\").toFile();\n    }\n\n    static void getLogs(ParcelFileDescriptor zipFd) throws IllegalStateException {\n        try (zipFd; var os = new ZipOutputStream(new FileOutputStream(zipFd.getFileDescriptor()))) {\n            var comment = String.format(Locale.ROOT, \"LSPosed %s %s (%d)\",\n                    BuildConfig.BUILD_TYPE, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE);\n            os.setComment(comment);\n            os.setLevel(Deflater.BEST_COMPRESSION);\n            zipAddDir(os, logDirPath);\n            zipAddDir(os, oldLogDirPath);\n            zipAddDir(os, Paths.get(\"/data/tombstones\"));\n            zipAddDir(os, Paths.get(\"/data/anr\"));\n            var data = Paths.get(\"/data/data\");\n            var app1 = data.resolve(BuildConfig.MANAGER_INJECTED_PKG_NAME + \"/cache/crash\");\n            var app2 = data.resolve(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME + \"/cache/crash\");\n            zipAddDir(os, app1);\n            zipAddDir(os, app2);\n            zipAddProcOutput(os, \"full.log\", \"logcat\", \"-b\", \"all\", \"-d\");\n            zipAddProcOutput(os, \"dmesg.log\", \"dmesg\");\n            var magiskDataDir = Paths.get(\"/data/adb\");\n            try (var l = Files.list(magiskDataDir.resolve(\"modules\"))) {\n                l.forEach(p -> {\n                    zipAddFile(os, p, magiskDataDir);\n                    zipAddFile(os, p.resolve(\"module.prop\"), magiskDataDir);\n                    zipAddFile(os, p.resolve(\"remove\"), magiskDataDir);\n                    zipAddFile(os, p.resolve(\"disable\"), magiskDataDir);\n                    zipAddFile(os, p.resolve(\"update\"), magiskDataDir);\n                    zipAddFile(os, p.resolve(\"sepolicy.rule\"), magiskDataDir);\n                });\n            }\n            var proc = Paths.get(\"/proc\");\n            for (var pid : new String[]{\"self\", String.valueOf(Binder.getCallingPid())}) {\n                var pidPath = proc.resolve(pid);\n                zipAddFile(os, pidPath.resolve(\"maps\"), proc);\n                zipAddFile(os, pidPath.resolve(\"mountinfo\"), proc);\n                zipAddFile(os, pidPath.resolve(\"status\"), proc);\n            }\n            zipAddFile(os, dbPath.toPath(), configDirPath);\n            ConfigManager.getInstance().exportScopes(os);\n        } catch (Throwable e) {\n            Log.w(TAG, \"get log\", e);\n            throw new IllegalStateException(e);\n        }\n    }\n\n    private static void zipAddProcOutput(ZipOutputStream os, String name, String... command) {\n        try (var is = new ProcessBuilder(command).start().getInputStream()) {\n            os.putNextEntry(new ZipEntry(name));\n            transfer(is, os);\n            os.closeEntry();\n        } catch (IOException e) {\n            Log.w(TAG, name, e);\n        }\n    }\n\n    private static void zipAddFile(ZipOutputStream os, Path path, Path base) {\n        var name = base.relativize(path).toString();\n        if (Files.isDirectory(path)) {\n            try {\n                os.putNextEntry(new ZipEntry(name + \"/\"));\n                os.closeEntry();\n            } catch (IOException e) {\n                Log.w(TAG, name, e);\n            }\n        } else if (Files.exists(path)) {\n            try (var is = new FileInputStream(path.toFile())) {\n                os.putNextEntry(new ZipEntry(name));\n                transfer(is, os);\n                os.closeEntry();\n            } catch (IOException e) {\n                Log.w(TAG, name, e);\n            }\n        }\n    }\n\n    private static void zipAddDir(ZipOutputStream os, Path path) throws IOException {\n        if (!Files.isDirectory(path)) return;\n        Files.walkFileTree(path, new SimpleFileVisitor<>() {\n            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {\n                if (Files.isRegularFile(file)) {\n                    var name = path.getParent().relativize(file).toString();\n                    try (var is = new FileInputStream(file.toFile())) {\n                        os.putNextEntry(new ZipEntry(name));\n                        transfer(is, os);\n                        os.closeEntry();\n                    } catch (IOException e) {\n                        Log.w(TAG, name, e);\n                    }\n                }\n                return FileVisitResult.CONTINUE;\n            }\n        });\n    }\n\n    private static SharedMemory readDex(InputStream in, boolean obfuscate) throws IOException, ErrnoException {\n        var memory = SharedMemory.create(null, in.available());\n        var byteBuffer = memory.mapReadWrite();\n        Channels.newChannel(in).read(byteBuffer);\n        SharedMemory.unmap(byteBuffer);\n        if (obfuscate) {\n            var newMemory = ObfuscationManager.obfuscateDex(memory);\n            if (memory != newMemory) {\n                memory.close();\n                memory = newMemory;\n            }\n        }\n        memory.setProtect(OsConstants.PROT_READ);\n        return memory;\n    }\n\n    private static void readDexes(ZipFile apkFile, List<SharedMemory> preLoadedDexes,\n                                  boolean obfuscate) {\n        int secondary = 2;\n        for (var dexFile = apkFile.getEntry(\"classes.dex\"); dexFile != null;\n             dexFile = apkFile.getEntry(\"classes\" + secondary + \".dex\"), secondary++) {\n            try (var is = apkFile.getInputStream(dexFile)) {\n                preLoadedDexes.add(readDex(is, obfuscate));\n            } catch (IOException | ErrnoException e) {\n                Log.w(TAG, \"Can not load \" + dexFile + \" in \" + apkFile, e);\n            }\n        }\n    }\n\n    private static void readName(ZipFile apkFile, String initName, List<String> names) {\n        var initEntry = apkFile.getEntry(initName);\n        if (initEntry == null) return;\n        try (var in = apkFile.getInputStream(initEntry)) {\n            var reader = new BufferedReader(new InputStreamReader(in));\n            String name;\n            while ((name = reader.readLine()) != null) {\n                name = name.trim();\n                if (name.isEmpty() || name.startsWith(\"#\")) continue;\n                names.add(name);\n            }\n        } catch (IOException | OutOfMemoryError e) {\n            Log.e(TAG, \"Can not open \" + initEntry, e);\n        }\n    }\n\n    @Nullable\n    static PreLoadedApk loadModule(String path, boolean obfuscate) {\n        if (path == null) return null;\n        var file = new PreLoadedApk();\n        var preLoadedDexes = new ArrayList<SharedMemory>();\n        var moduleClassNames = new ArrayList<String>(1);\n        var moduleLibraryNames = new ArrayList<String>(1);\n        try (var apkFile = new ZipFile(toGlobalNamespace(path))) {\n            readDexes(apkFile, preLoadedDexes, obfuscate);\n            readName(apkFile, \"META-INF/xposed/java_init.list\", moduleClassNames);\n            if (moduleClassNames.isEmpty()) {\n                file.legacy = true;\n                readName(apkFile, \"assets/xposed_init\", moduleClassNames);\n                readName(apkFile, \"assets/native_init\", moduleLibraryNames);\n            } else {\n                file.legacy = false;\n                readName(apkFile, \"META-INF/xposed/native_init.list\", moduleLibraryNames);\n            }\n        } catch (IOException e) {\n            Log.e(TAG, \"Can not open \" + path, e);\n            return null;\n        }\n        if (preLoadedDexes.isEmpty()) return null;\n        if (moduleClassNames.isEmpty()) return null;\n\n        if (obfuscate) {\n            var signatures = ObfuscationManager.getSignatures();\n            for (int i = 0; i < moduleClassNames.size(); i++) {\n                var s = moduleClassNames.get(i);\n                for (var entry : signatures.entrySet()) {\n                    if (s.startsWith(entry.getKey())) {\n                        moduleClassNames.add(i, s.replace(entry.getKey(), entry.getValue()));\n                    }\n                }\n            }\n        }\n\n        file.preLoadedDexes = preLoadedDexes;\n        file.moduleClassNames = moduleClassNames;\n        file.moduleLibraryNames = moduleLibraryNames;\n        return file;\n    }\n\n    static boolean tryLock() {\n        var openOptions = new HashSet<OpenOption>();\n        openOptions.add(StandardOpenOption.CREATE);\n        openOptions.add(StandardOpenOption.WRITE);\n        var p = PosixFilePermissions.fromString(\"rw-------\");\n        var permissions = PosixFilePermissions.asFileAttribute(p);\n\n        try {\n            var lockChannel = FileChannel.open(lockPath, openOptions, permissions);\n            locker = new FileLocker(lockChannel);\n            return locker.isValid();\n        } catch (Throwable e) {\n            return false;\n        }\n    }\n\n    synchronized static SharedMemory getPreloadDex(boolean obfuscate) {\n        if (preloadDex == null) {\n            try (var is = new FileInputStream(\"framework/lspd.dex\")) {\n                preloadDex = readDex(is, obfuscate);\n            } catch (Throwable e) {\n                Log.e(TAG, \"preload dex\", e);\n            }\n        }\n        return preloadDex;\n    }\n\n    static void ensureModuleFilePath(String path) throws RemoteException {\n        if (path == null || path.indexOf(File.separatorChar) >= 0 || \".\".equals(path) || \"..\".equals(path)) {\n            throw new RemoteException(\"Invalid path: \" + path);\n        }\n    }\n\n    static Path resolveModuleDir(String packageName, String dir, int userId, int uid) throws IOException {\n        var path = modulePath.resolve(String.valueOf(userId)).resolve(packageName).resolve(dir).normalize();\n        // Ensure the directory and any necessary parent directories exist.\n        path.toFile().mkdirs();\n\n        if (SELinux.getFileContext(path.toString()) != \"u:object_r:xposed_data:s0\") {\n            // SELinux label could be reset after a reboot.\n            try {\n                setSelinuxContextRecursive(path, \"u:object_r:xposed_data:s0\");\n                Os.chown(path.toString(), uid, uid);\n                Os.chmod(path.toString(), 0755);\n            } catch (ErrnoException e) {\n                throw new IOException(e);\n            }\n        }\n        return path;\n    }\n\n     private static void setSelinuxContextRecursive(Path path, String context) throws IOException {\n        try {\n            SELinux.setFileContext(path.toString(), context);\n\n            if (Files.isDirectory(path)) {\n                try (Stream<Path> stream = Files.list(path)) {\n                    for (Path entry : (Iterable<Path>) stream::iterator) {\n                        setSelinuxContextRecursive(entry, context);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            throw new IOException(\"Failed to recursively set SELinux context for \" + path, e);\n        }\n    }\n\n    private static class FileLocker {\n        private final FileChannel lockChannel;\n        private final FileLock locker;\n\n        FileLocker(FileChannel lockChannel) throws IOException {\n            this.lockChannel = lockChannel;\n            this.locker = lockChannel.tryLock();\n        }\n\n        boolean isValid() {\n            return this.locker != null && this.locker.isValid();\n        }\n\n        @Override\n        protected void finalize() throws Throwable {\n            this.locker.release();\n            this.lockChannel.close();\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.PackageService.MATCH_ALL_FLAGS;\nimport static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\nimport static org.lsposed.lspd.service.ServiceManager.existsInGlobalNamespace;\nimport static org.lsposed.lspd.service.ServiceManager.toGlobalNamespace;\n\nimport android.annotation.SuppressLint;\nimport android.content.ContentValues;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageParser;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteException;\nimport android.database.sqlite.SQLiteStatement;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.ParcelFileDescriptor;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.os.SELinux;\nimport android.os.SharedMemory;\nimport android.os.SystemClock;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.apache.commons.lang3.SerializationUtilsX;\nimport org.lsposed.daemon.BuildConfig;\nimport org.lsposed.lspd.models.Application;\nimport org.lsposed.lspd.models.Module;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.nio.file.attribute.PosixFilePermissions;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipOutputStream;\n\nimport hidden.HiddenApiBridge;\n\npublic class ConfigManager {\n    private static ConfigManager instance = null;\n\n    private final SQLiteDatabase db = openDb();\n\n    private boolean verboseLog = true;\n    private boolean logWatchdog = true;\n    private boolean dexObfuscate = true;\n    private boolean enableStatusNotification = true;\n    private Path miscPath = null;\n\n    private int managerUid = -1;\n\n    private final Handler cacheHandler;\n\n    private long lastModuleCacheTime = 0;\n    private long requestModuleCacheTime = 0;\n\n    private long lastScopeCacheTime = 0;\n    private long requestScopeCacheTime = 0;\n\n    private String api = \"(???)\";\n\n    static class ProcessScope {\n        final String processName;\n        final int uid;\n\n        ProcessScope(@NonNull String processName, int uid) {\n            this.processName = processName;\n            this.uid = uid;\n        }\n\n        @Override\n        public boolean equals(@Nullable Object o) {\n            if (o instanceof ProcessScope) {\n                ProcessScope p = (ProcessScope) o;\n                return p.processName.equals(processName) && p.uid == uid;\n            }\n            return false;\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hashCode(processName) ^ uid;\n        }\n    }\n\n    private static final String CREATE_MODULES_TABLE = \"CREATE TABLE IF NOT EXISTS modules (\" +\n            \"mid integer PRIMARY KEY AUTOINCREMENT,\" +\n            \"module_pkg_name text NOT NULL UNIQUE,\" +\n            \"apk_path text NOT NULL, \" +\n            \"enabled BOOLEAN DEFAULT 0 \" +\n            \"CHECK (enabled IN (0, 1))\" +\n            \");\";\n\n    private static final String CREATE_SCOPE_TABLE = \"CREATE TABLE IF NOT EXISTS scope (\" +\n            \"mid integer,\" +\n            \"app_pkg_name text NOT NULL,\" +\n            \"user_id integer NOT NULL,\" +\n            \"PRIMARY KEY (mid, app_pkg_name, user_id),\" +\n            \"CONSTRAINT scope_module_constraint\" +\n            \"  FOREIGN KEY (mid)\" +\n            \"  REFERENCES modules (mid)\" +\n            \"  ON DELETE CASCADE\" +\n            \");\";\n\n    private static final String CREATE_CONFIG_TABLE = \"CREATE TABLE IF NOT EXISTS configs (\" +\n            \"module_pkg_name text NOT NULL,\" +\n            \"user_id integer NOT NULL,\" +\n            \"`group` text NOT NULL,\" +\n            \"`key` text NOT NULL,\" +\n            \"data blob NOT NULL,\" +\n            \"PRIMARY KEY (module_pkg_name, user_id, `group`, `key`),\" +\n            \"CONSTRAINT config_module_constraint\" +\n            \"  FOREIGN KEY (module_pkg_name)\" +\n            \"  REFERENCES modules (module_pkg_name)\" +\n            \"  ON DELETE CASCADE\" +\n            \");\";\n\n    private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();\n\n    // packageName, Module\n    private final Map<String, Module> cachedModule = new ConcurrentHashMap<>();\n\n    // packageName, userId, group, key, value\n    private final Map<Pair<String, Integer>, Map<String, HashMap<String, Object>>> cachedConfig = new ConcurrentHashMap<>();\n\n    private Set<String> scopeRequestBlocked = new HashSet<>();\n\n    private static SQLiteDatabase openDb() {\n        var params = new SQLiteDatabase.OpenParams.Builder()\n                .addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING)\n                .setErrorHandler(sqLiteDatabase -> Log.w(TAG, \"database corrupted\"));\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            params.setSynchronousMode(\"NORMAL\");\n        }\n        return SQLiteDatabase.openDatabase(ConfigFileManager.dbPath.getAbsoluteFile(), params.build());\n    }\n\n    private void updateCaches(boolean sync) {\n        synchronized (cacheHandler) {\n            requestScopeCacheTime = requestModuleCacheTime = SystemClock.elapsedRealtime();\n        }\n        if (sync) {\n            cacheModules();\n        } else {\n            cacheHandler.post(this::cacheModules);\n        }\n    }\n\n    // for system server, cache is not yet ready, we need to query database for it\n    public boolean shouldSkipSystemServer() {\n        if (!SELinux.checkSELinuxAccess(\"u:r:system_server:s0\", \"u:r:system_server:s0\", \"process\", \"execmem\")) {\n            Log.e(TAG, \"skip injecting into android because sepolicy was not loaded properly\");\n            return true; // skip\n        }\n        /*\n        try (Cursor cursor = db.query(\"scope INNER JOIN modules ON scope.mid = modules.mid\", new String[]{\"modules.mid\"}, \"app_pkg_name=? AND enabled=1\", new String[]{\"system\"}, null, null, null)) {\n            return cursor == null || !cursor.moveToNext();\n        }*/\n        return false;\n    }\n\n    @SuppressLint(\"BlockedPrivateApi\")\n    public List<Module> getModulesForSystemServer() {\n        List<Module> modules = new LinkedList<>();\n        try (Cursor cursor = db.query(\"scope INNER JOIN modules ON scope.mid = modules.mid\", new String[]{\"module_pkg_name\", \"apk_path\"}, \"app_pkg_name=? AND enabled=1\", new String[]{\"system\"}, null, null, null)) {\n            int apkPathIdx = cursor.getColumnIndex(\"apk_path\");\n            int pkgNameIdx = cursor.getColumnIndex(\"module_pkg_name\");\n            while (cursor.moveToNext()) {\n                var module = new Module();\n                module.apkPath = cursor.getString(apkPathIdx);\n                module.packageName = cursor.getString(pkgNameIdx);\n                var cached = cachedModule.get(module.packageName);\n                if (cached != null) {\n                    modules.add(cached);\n                    continue;\n                }\n                var statPath = toGlobalNamespace(\"/data/user_de/0/\" + module.packageName).getAbsolutePath();\n                try {\n                    module.appId = Os.stat(statPath).st_uid;\n                } catch (ErrnoException e) {\n                    Log.w(TAG, \"cannot stat \" + statPath, e);\n                    module.appId = -1;\n                }\n                try {\n                    var apkFile = new File(module.apkPath);\n                    var pkg = new PackageParser().parsePackage(apkFile, 0, false);\n                    module.applicationInfo = pkg.applicationInfo;\n                    module.applicationInfo.sourceDir = module.apkPath;\n                    module.applicationInfo.dataDir = statPath;\n                    module.applicationInfo.deviceProtectedDataDir = statPath;\n                    HiddenApiBridge.ApplicationInfo_credentialProtectedDataDir(module.applicationInfo, statPath);\n                    module.applicationInfo.processName = module.packageName;\n                } catch (PackageParser.PackageParserException e) {\n                    Log.w(TAG, \"failed to parse \" + module.apkPath, e);\n                }\n                module.service = new LSPInjectedModuleService(module.packageName);\n                modules.add(module);\n            }\n        }\n\n        return modules.parallelStream().filter(m -> {\n            var file = ConfigFileManager.loadModule(m.apkPath, dexObfuscate);\n            if (file == null) {\n                Log.w(TAG, \"Can not load \" + m.apkPath + \", skip!\");\n                return false;\n            }\n            m.file = file;\n            cachedModule.putIfAbsent(m.packageName, m);\n            return true;\n        }).collect(Collectors.toList());\n    }\n\n    private synchronized void updateConfig() {\n        Map<String, Object> config = getModulePrefs(\"lspd\", 0, \"config\");\n\n        Object bool = config.get(\"enable_verbose_log\");\n        verboseLog = bool == null || (boolean) bool;\n\n        bool = config.get(\"enable_log_watchdog\");\n        logWatchdog = bool == null || (boolean) bool;\n\n        bool = config.get(\"enable_dex_obfuscate\");\n        dexObfuscate = bool == null || (boolean) bool;\n\n        bool = config.get(\"enable_auto_add_shortcut\");\n        if (bool != null) {\n            // TODO: remove\n            updateModulePrefs(\"lspd\", 0, \"config\", \"enable_auto_add_shortcut\", null);\n        }\n\n        bool = config.get(\"enable_status_notification\");\n        enableStatusNotification = bool == null || (boolean) bool;\n\n        var set = (Set<String>) config.get(\"scope_request_blocked\");\n        scopeRequestBlocked = set == null ? new HashSet<>() : set;\n\n        // Don't migrate to ConfigFileManager, as XSharedPreferences will be restored soon\n        String string = (String) config.get(\"misc_path\");\n        if (string == null) {\n            miscPath = Paths.get(\"/data\", \"misc\", UUID.randomUUID().toString());\n            updateModulePrefs(\"lspd\", 0, \"config\", \"misc_path\", miscPath.toString());\n        } else {\n            miscPath = Paths.get(string);\n        }\n        try {\n            var perms = PosixFilePermissions.fromString(\"rwx--x--x\");\n            Files.createDirectories(miscPath, PosixFilePermissions.asFileAttribute(perms));\n            walkFileTree(miscPath, f -> SELinux.setFileContext(f.toString(), \"u:object_r:xposed_data:s0\"));\n        } catch (IOException e) {\n            Log.e(TAG, Log.getStackTraceString(e));\n        }\n\n        updateManager(false);\n\n        cacheHandler.post(this::getPreloadDex);\n    }\n\n    public synchronized void updateManager(boolean uninstalled) {\n        if (uninstalled) {\n            managerUid = -1;\n            return;\n        }\n        if (!PackageService.isAlive()) return;\n        try {\n            PackageInfo info = PackageService.getPackageInfo(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME, 0, 0);\n            if (info != null) {\n                managerUid = info.applicationInfo.uid;\n            } else {\n                managerUid = -1;\n                Log.i(TAG, \"manager is not installed\");\n            }\n        } catch (RemoteException ignored) {\n        }\n    }\n\n    static ConfigManager getInstance() {\n        if (instance == null)\n            instance = new ConfigManager();\n        boolean needCached;\n        synchronized (instance.cacheHandler) {\n            needCached = instance.lastModuleCacheTime == 0 || instance.lastScopeCacheTime == 0;\n        }\n        if (needCached) {\n            if (PackageService.isAlive() && UserService.isAlive()) {\n                Log.d(TAG, \"pm & um are ready, updating cache\");\n                // must ensure cache is valid for later usage\n                instance.updateCaches(true);\n                instance.updateManager(false);\n            }\n        }\n        return instance;\n    }\n\n    private ConfigManager() {\n        HandlerThread cacheThread = new HandlerThread(\"cache\");\n        cacheThread.start();\n        cacheHandler = new Handler(cacheThread.getLooper());\n\n        initDB();\n        updateConfig();\n        // must ensure cache is valid for later usage\n        updateCaches(true);\n    }\n\n\n    private <T> T executeInTransaction(Supplier<T> execution) {\n        try {\n            db.beginTransaction();\n            var res = execution.get();\n            db.setTransactionSuccessful();\n            return res;\n        } finally {\n            db.endTransaction();\n        }\n    }\n\n    private void executeInTransaction(Runnable execution) {\n        executeInTransaction((Supplier<Void>) () -> {\n            execution.run();\n            return null;\n        });\n    }\n\n    private void initDB() {\n        db.setForeignKeyConstraintsEnabled(true);\n        int oldVersion = db.getVersion();\n        if (oldVersion >= 4) {\n            // Database is already up to date.\n            return;\n        }\n\n        Log.i(TAG, \"Initializing/Upgrading database from version \" + oldVersion + \" to 4\");\n        db.beginTransaction();\n        try {\n            if (oldVersion == 0) {\n                db.execSQL(CREATE_MODULES_TABLE);\n                db.execSQL(CREATE_SCOPE_TABLE);\n                db.execSQL(CREATE_CONFIG_TABLE);\n\n                var values = new ContentValues();\n                values.put(\"module_pkg_name\", \"lspd\");\n                values.put(\"apk_path\", ConfigFileManager.managerApkPath.toString());\n                db.insertWithOnConflict(\"modules\", null, values, SQLiteDatabase.CONFLICT_IGNORE);\n                oldVersion = 1;\n            }\n            if (oldVersion < 2) {\n                // Upgrade from 1 to 2: Recreate tables to enforce constraints and clean up.\n                db.compileStatement(\"DROP INDEX IF EXISTS configs_idx;\").execute();\n                db.compileStatement(\"DROP TABLE IF EXISTS config;\").execute();\n                db.compileStatement(\"ALTER TABLE scope RENAME TO old_scope;\").execute();\n                db.compileStatement(\"ALTER TABLE configs RENAME TO old_configs;\").execute();\n\n                db.execSQL(CREATE_SCOPE_TABLE);\n                db.execSQL(CREATE_CONFIG_TABLE);\n\n                try {\n                    db.compileStatement(\"INSERT INTO scope SELECT * FROM old_scope;\").execute();\n                } catch (Throwable e) {\n                    Log.w(TAG, \"Failed to migrate scope data\", e);\n                }\n                try {\n                    db.compileStatement(\"INSERT INTO configs SELECT * FROM old_configs;\").execute();\n                } catch (Throwable e) {\n                    Log.w(TAG, \"Failed to migrate config data\", e);\n                }\n\n                db.compileStatement(\"DROP TABLE old_scope;\").execute();\n                db.compileStatement(\"DROP TABLE old_configs;\").execute();\n                db.compileStatement(\"CREATE INDEX IF NOT EXISTS configs_idx ON configs (module_pkg_name, user_id);\").execute();\n            }\n            if (oldVersion < 3) {\n                // Upgrade from 2 to 3: Rename 'android' scope to 'system'.\n                db.compileStatement(\"UPDATE scope SET app_pkg_name = 'system' WHERE app_pkg_name = 'android';\").execute();\n            }\n            if (oldVersion < 4) {\n                // Upgrade from 3 to 4: Add the 'auto_include' column to the modules table.\n                try {\n                    db.compileStatement(\"ALTER TABLE modules ADD COLUMN auto_include BOOLEAN DEFAULT 0 CHECK (auto_include IN (0, 1));\").execute();\n                } catch (SQLiteException ex) {\n                    // This might happen if the column already exists from a previous buggy run.\n                    Log.w(TAG, \"Could not add auto_include column, it may already exist.\", ex);\n                }\n            }\n            db.setVersion(4);\n            db.setTransactionSuccessful();\n            Log.i(TAG, \"Database upgrade to version 4 successful.\");\n        } catch (Throwable e) {\n            Log.e(TAG, \"Failed to initialize or upgrade database, transaction rolled back.\", e);\n        } finally {\n            db.endTransaction();\n        }\n    }\n\n    private List<ProcessScope> getAssociatedProcesses(Application app) throws RemoteException {\n        Pair<Set<String>, Integer> result = PackageService.fetchProcessesWithUid(app);\n        List<ProcessScope> processes = new ArrayList<>();\n        if (app.packageName.equals(\"android\")) {\n            // this is hardcoded for ResolverActivity\n            processes.add(new ProcessScope(\"system:ui\", Process.SYSTEM_UID));\n        }\n        for (String processName : result.first) {\n            var uid = result.second;\n            if (uid == Process.SYSTEM_UID && processName.equals(\"system\")) {\n                // code run in system_server\n                continue;\n            }\n            processes.add(new ProcessScope(processName, uid));\n        }\n        return processes;\n    }\n\n    private @NonNull\n    Map<String, HashMap<String, Object>> fetchModuleConfig(String name, int user_id) {\n        var config = new ConcurrentHashMap<String, HashMap<String, Object>>();\n\n        try (Cursor cursor = db.query(\"configs\", new String[]{\"`group`\", \"`key`\", \"data\"},\n                \"module_pkg_name = ? and user_id = ?\", new String[]{name, String.valueOf(user_id)}, null, null, null)) {\n            if (cursor == null) {\n                Log.e(TAG, \"db cache failed\");\n                return config;\n            }\n            int groupIdx = cursor.getColumnIndex(\"group\");\n            int keyIdx = cursor.getColumnIndex(\"key\");\n            int dataIdx = cursor.getColumnIndex(\"data\");\n            while (cursor.moveToNext()) {\n                var group = cursor.getString(groupIdx);\n                var key = cursor.getString(keyIdx);\n                var data = cursor.getBlob(dataIdx);\n                var object = SerializationUtilsX.deserialize(data);\n                if (object == null) continue;\n                config.computeIfAbsent(group, g -> new HashMap<>()).put(key, object);\n            }\n        }\n        return config;\n    }\n\n    public void updateModulePrefs(String moduleName, int userId, String group, String key, Object value) {\n        Map<String, Object> values = new HashMap<>();\n        values.put(key, value);\n        updateModulePrefs(moduleName, userId, group, values);\n    }\n\n    public void updateModulePrefs(String moduleName, int userId, String group, Map<String, Object> values) {\n        var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));\n        config.compute(group, (g, prefs) -> {\n            HashMap<String, Object> newPrefs = prefs == null ? new HashMap<>() : new HashMap<>(prefs);\n            executeInTransaction(() -> {\n                for (var entry : values.entrySet()) {\n                    var key = entry.getKey();\n                    var value = entry.getValue();\n                    if (value instanceof Serializable) {\n                        newPrefs.put(key, value);\n                        var contents = new ContentValues();\n                        contents.put(\"`group`\", group);\n                        contents.put(\"`key`\", key);\n                        contents.put(\"data\", SerializationUtilsX.serialize((Serializable) value));\n                        contents.put(\"module_pkg_name\", moduleName);\n                        contents.put(\"user_id\", String.valueOf(userId));\n                        db.insertWithOnConflict(\"configs\", null, contents, SQLiteDatabase.CONFLICT_REPLACE);\n                    } else {\n                        newPrefs.remove(key);\n                        db.delete(\"configs\", \"module_pkg_name=? and user_id=? and `group`=? and `key`=?\", new String[]{moduleName, String.valueOf(userId), group, key});\n                    }\n                }\n                var bundle = new Bundle();\n                bundle.putSerializable(\"config\", (Serializable) config);\n                if (bundle.size() > 1024 * 1024) {\n                    throw new IllegalArgumentException(\"Preference too large\");\n                }\n            });\n            return newPrefs;\n        });\n    }\n\n    public void deleteModulePrefs(String moduleName, int userId, String group) {\n        db.delete(\"configs\", \"module_pkg_name=? and user_id=? and `group`=?\", new String[]{moduleName, String.valueOf(userId), group});\n        var config = cachedConfig.getOrDefault(new Pair<>(moduleName, userId), null);\n        if (config != null) {\n            config.remove(group);\n        }\n    }\n\n    public HashMap<String, Object> getModulePrefs(String moduleName, int userId, String group) {\n        var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));\n        return config.getOrDefault(group, new HashMap<>());\n    }\n\n    private synchronized void clearCache() {\n        synchronized (cacheHandler) {\n            lastScopeCacheTime = 0;\n            lastModuleCacheTime = 0;\n        }\n        cachedModule.clear();\n        cachedScope.clear();\n    }\n\n    private synchronized void cacheModules() {\n        // skip caching when pm is not yet available\n        if (!PackageService.isAlive() || !UserService.isAlive()) return;\n        synchronized (cacheHandler) {\n            if (lastModuleCacheTime >= requestModuleCacheTime) return;\n            else lastModuleCacheTime = SystemClock.elapsedRealtime();\n        }\n        Set<SharedMemory> toClose = ConcurrentHashMap.newKeySet();\n        try (Cursor cursor = db.query(true, \"modules\", new String[]{\"module_pkg_name\", \"apk_path\"},\n                \"enabled = 1\", null, null, null, null, null)) {\n            if (cursor == null) {\n                Log.e(TAG, \"db cache failed\");\n                return;\n            }\n            int pkgNameIdx = cursor.getColumnIndex(\"module_pkg_name\");\n            int apkPathIdx = cursor.getColumnIndex(\"apk_path\");\n            Set<String> obsoleteModules = ConcurrentHashMap.newKeySet();\n            // packageName, apkPath\n            Map<String, String> obsoletePaths = new ConcurrentHashMap<>();\n            cachedModule.values().removeIf(m -> {\n                if (m.apkPath == null || !existsInGlobalNamespace(m.apkPath)) {\n                    toClose.addAll(m.file.preLoadedDexes);\n                    return true;\n                }\n                return false;\n            });\n            List<Module> modules = new ArrayList<>();\n            while (cursor.moveToNext()) {\n                String packageName = cursor.getString(pkgNameIdx);\n                String apkPath = cursor.getString(apkPathIdx);\n                if (packageName.equals(\"lspd\")) continue;\n                var module = new Module();\n                module.packageName = packageName;\n                module.apkPath = apkPath;\n                modules.add(module);\n            }\n\n            modules.stream().parallel().filter(m -> {\n                var oldModule = cachedModule.get(m.packageName);\n                PackageInfo pkgInfo = null;\n                try {\n                    pkgInfo = PackageService.getPackageInfoFromAllUsers(m.packageName, MATCH_ALL_FLAGS).values().stream().findFirst().orElse(null);\n                } catch (Throwable e) {\n                    Log.w(TAG, \"Get package info of \" + m.packageName, e);\n                }\n                if (pkgInfo == null || pkgInfo.applicationInfo == null) {\n                    Log.w(TAG, \"Failed to find package info of \" + m.packageName);\n                    obsoleteModules.add(m.packageName);\n                    return false;\n                }\n\n                if (oldModule != null &&\n                        pkgInfo.applicationInfo.sourceDir != null &&\n                        m.apkPath != null && oldModule.apkPath != null &&\n                        existsInGlobalNamespace(m.apkPath) &&\n                        Objects.equals(m.apkPath, oldModule.apkPath) &&\n                        Objects.equals(new File(pkgInfo.applicationInfo.sourceDir).getParent(), new File(m.apkPath).getParent())) {\n                    if (oldModule.appId != -1) {\n                        Log.d(TAG, m.packageName + \" did not change, skip caching it\");\n                    } else {\n                        // cache from system server, update application info\n                        oldModule.applicationInfo = pkgInfo.applicationInfo;\n                    }\n                    return false;\n                }\n                m.apkPath = getModuleApkPath(pkgInfo.applicationInfo);\n                if (m.apkPath == null) {\n                    Log.w(TAG, \"Failed to find path of \" + m.packageName);\n                    obsoleteModules.add(m.packageName);\n                    return false;\n                } else {\n                    obsoletePaths.put(m.packageName, m.apkPath);\n                }\n                m.appId = pkgInfo.applicationInfo.uid;\n                m.applicationInfo = pkgInfo.applicationInfo;\n                m.service = oldModule != null ? oldModule.service : new LSPInjectedModuleService(m.packageName);\n                return true;\n            }).forEach(m -> {\n                var file = ConfigFileManager.loadModule(m.apkPath, dexObfuscate);\n                if (file == null) {\n                    Log.w(TAG, \"failed to load module \" + m.packageName);\n                    obsoleteModules.add(m.packageName);\n                    return;\n                }\n                m.file = file;\n                cachedModule.put(m.packageName, m);\n            });\n\n            if (PackageService.isAlive()) {\n                obsoleteModules.forEach(this::removeModuleWithoutCache);\n                obsoletePaths.forEach((packageName, path) -> updateModuleApkPath(packageName, path, true));\n            } else {\n                Log.w(TAG, \"pm is dead while caching. invalidating...\");\n                clearCache();\n                return;\n            }\n        }\n        Log.d(TAG, \"cached modules\");\n        for (var module : cachedModule.entrySet()) {\n            Log.d(TAG, module.getKey() + \" \" + module.getValue().apkPath);\n        }\n        cacheScopes();\n        toClose.forEach(SharedMemory::close);\n    }\n\n    private synchronized void cacheScopes() {\n        // skip caching when pm is not yet available\n        if (!PackageService.isAlive()) return;\n        synchronized (cacheHandler) {\n            if (lastScopeCacheTime >= requestScopeCacheTime) return;\n            else lastScopeCacheTime = SystemClock.elapsedRealtime();\n        }\n        cachedScope.clear();\n        try (Cursor cursor = db.query(\"scope INNER JOIN modules ON scope.mid = modules.mid\", new String[]{\"app_pkg_name\", \"module_pkg_name\", \"user_id\"},\n                \"enabled = 1\", null, null, null, null)) {\n            int appPkgNameIdx = cursor.getColumnIndex(\"app_pkg_name\");\n            int modulePkgNameIdx = cursor.getColumnIndex(\"module_pkg_name\");\n            int userIdIdx = cursor.getColumnIndex(\"user_id\");\n\n            final var obsoletePackages = new HashSet<Application>();\n            final var obsoleteModules = new HashSet<Application>();\n            final var moduleAvailability = new HashMap<Pair<String, Integer>, Boolean>();\n            final var cachedProcessScope = new HashMap<Pair<String, Integer>, List<ProcessScope>>();\n\n            final var denylist = new HashSet<>(getDenyListPackages());\n            while (cursor.moveToNext()) {\n                Application app = new Application();\n                app.packageName = cursor.getString(appPkgNameIdx);\n                app.userId = cursor.getInt(userIdIdx);\n                var modulePackageName = cursor.getString(modulePkgNameIdx);\n\n                // check if module is present in this user\n                if (!moduleAvailability.computeIfAbsent(new Pair<>(modulePackageName, app.userId), n -> {\n                    var available = false;\n                    try {\n                        available = PackageService.isPackageAvailable(n.first, n.second, true) && cachedModule.containsKey(modulePackageName);\n                    } catch (Throwable e) {\n                        Log.w(TAG, \"check package availability \", e);\n                    }\n                    if (!available) {\n                        var obsoleteModule = new Application();\n                        obsoleteModule.packageName = modulePackageName;\n                        obsoleteModule.userId = app.userId;\n                        obsoleteModules.add(obsoleteModule);\n                    }\n                    return available;\n                })) continue;\n\n                // system server always loads database\n                if (app.packageName.equals(\"system\")) continue;\n\n                try {\n                    List<ProcessScope> processesScope = cachedProcessScope.computeIfAbsent(new Pair<>(app.packageName, app.userId), (k) -> {\n                        try {\n                            if (denylist.contains(app.packageName))\n                                Log.w(TAG, app.packageName + \" is on denylist. It may not take effect.\");\n                            return getAssociatedProcesses(app);\n                        } catch (RemoteException e) {\n                            return Collections.emptyList();\n                        }\n                    });\n                    if (processesScope.isEmpty()) {\n                        obsoletePackages.add(app);\n                        continue;\n                    }\n                    var module = cachedModule.get(modulePackageName);\n                    assert module != null;\n                    for (ProcessScope processScope : processesScope) {\n                        cachedScope.computeIfAbsent(processScope,\n                                ignored -> new LinkedList<>()).add(module);\n                        // Always allow the module to inject itself\n                        if (modulePackageName.equals(app.packageName)) {\n                            var appId = processScope.uid % PER_USER_RANGE;\n                            for (var user : UserService.getUsers()) {\n                                var moduleUid = user.id * PER_USER_RANGE + appId;\n                                if (moduleUid == processScope.uid) continue; // skip duplicate\n                                var moduleSelf = new ProcessScope(processScope.processName, moduleUid);\n                                cachedScope.computeIfAbsent(moduleSelf,\n                                        ignored -> new LinkedList<>()).add(module);\n                            }\n                        }\n                    }\n                } catch (RemoteException e) {\n                    Log.e(TAG, Log.getStackTraceString(e));\n                }\n            }\n            if (PackageService.isAlive()) {\n                for (Application obsoletePackage : obsoletePackages) {\n                    Log.d(TAG, \"removing obsolete package: \" + obsoletePackage.packageName + \"/\" + obsoletePackage.userId);\n                    removeAppWithoutCache(obsoletePackage);\n                }\n                for (Application obsoleteModule : obsoleteModules) {\n                    Log.d(TAG, \"removing obsolete module: \" + obsoleteModule.packageName + \"/\" + obsoleteModule.userId);\n                    removeModuleScopeWithoutCache(obsoleteModule);\n                    removeBlockedScopeRequest(obsoleteModule.packageName);\n                }\n            } else {\n                Log.w(TAG, \"pm is dead while caching. invalidating...\");\n                clearCache();\n                return;\n            }\n        }\n        Log.d(TAG, \"cached scope\");\n        cachedScope.forEach((ps, modules) -> {\n            Log.d(TAG, ps.processName + \"/\" + ps.uid);\n            modules.forEach(module -> Log.d(TAG, \"\\t\" + module.packageName));\n        });\n    }\n\n    // This is called when a new process created, use the cached result\n    public List<Module> getModulesForProcess(String processName, int uid) {\n        return isManager(uid) ? Collections.emptyList() : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptyList());\n    }\n\n    // This is called when a new process created, use the cached result\n    public boolean shouldSkipProcess(ProcessScope scope) {\n        return !cachedScope.containsKey(scope) && !isManager(scope.uid);\n    }\n\n    public boolean isUidHooked(int uid) {\n        return cachedScope.keySet().stream().reduce(false, (p, scope) -> p || scope.uid == uid, Boolean::logicalOr);\n    }\n\n    @Nullable\n    public List<Application> getModuleScope(String packageName) {\n        if (packageName.equals(\"lspd\")) return null;\n        try (Cursor cursor = db.query(\"scope INNER JOIN modules ON scope.mid = modules.mid\", new String[]{\"app_pkg_name\", \"user_id\"},\n                \"modules.module_pkg_name = ?\", new String[]{packageName}, null, null, null)) {\n            if (cursor == null) {\n                return null;\n            }\n            int userIdIdx = cursor.getColumnIndex(\"user_id\");\n            int appPkgNameIdx = cursor.getColumnIndex(\"app_pkg_name\");\n            ArrayList<Application> result = new ArrayList<>();\n            while (cursor.moveToNext()) {\n                Application scope = new Application();\n                scope.packageName = cursor.getString(appPkgNameIdx);\n                scope.userId = cursor.getInt(userIdIdx);\n                result.add(scope);\n            }\n            return result;\n        }\n    }\n\n    @Nullable\n    public String getModuleApkPath(ApplicationInfo info) {\n        String[] apks;\n        if (info.splitSourceDirs != null) {\n            apks = Arrays.copyOf(info.splitSourceDirs, info.splitSourceDirs.length + 1);\n            apks[info.splitSourceDirs.length] = info.sourceDir;\n        } else apks = new String[]{info.sourceDir};\n        var apkPath = Arrays.stream(apks).parallel().filter(apk -> {\n            if (apk == null) {\n                Log.w(TAG, info.packageName + \" has null apk path???\");\n                return false;\n            }\n            try (var zip = new ZipFile(toGlobalNamespace(apk))) {\n                return zip.getEntry(\"META-INF/xposed/java_init.list\") != null || zip.getEntry(\"assets/xposed_init\") != null;\n            } catch (IOException e) {\n                return false;\n            }\n        }).findFirst();\n        return apkPath.orElse(null);\n    }\n\n    public boolean updateModuleApkPath(String packageName, String apkPath, boolean force) {\n        if (apkPath == null || packageName.equals(\"lspd\")) return false;\n        if (db.inTransaction()) {\n            Log.w(TAG, \"update module apk path should not be called inside transaction\");\n            return false;\n        }\n\n        ContentValues values = new ContentValues();\n        values.put(\"module_pkg_name\", packageName);\n        values.put(\"apk_path\", apkPath);\n        // insert or update in two step since insert or replace will change the autoincrement mid\n        int count = (int) db.insertWithOnConflict(\"modules\", null, values, SQLiteDatabase.CONFLICT_IGNORE);\n        if (count < 0) {\n            var cached = cachedModule.getOrDefault(packageName, null);\n            if (force || cached == null || cached.apkPath == null || !cached.apkPath.equals(apkPath))\n                count = db.updateWithOnConflict(\"modules\", values, \"module_pkg_name=?\", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);\n            else\n                count = 0;\n        }\n        // force update is because cache is already update to date\n        // skip caching again\n        if (!force && count > 0) {\n            // Called by oneway binder\n            updateCaches(true);\n            return true;\n        }\n        return count > 0;\n    }\n\n    // Only be called before updating modules. No need to cache.\n    private int getModuleId(String packageName) {\n        if (packageName.equals(\"lspd\")) return -1;\n        if (db.inTransaction()) {\n            Log.w(TAG, \"get module id should not be called inside transaction\");\n            return -1;\n        }\n        try (Cursor cursor = db.query(\"modules\", new String[]{\"mid\"}, \"module_pkg_name=?\", new String[]{packageName}, null, null, null)) {\n            if (cursor == null) return -1;\n            if (cursor.getCount() != 1) return -1;\n            cursor.moveToFirst();\n            return cursor.getInt(cursor.getColumnIndexOrThrow(\"mid\"));\n        }\n    }\n\n    public boolean setModuleScope(String packageName, List<Application> scopes) throws RemoteException {\n        if (scopes == null) return false;\n        enableModule(packageName);\n        int mid = getModuleId(packageName);\n        if (mid == -1) return false;\n        executeInTransaction(() -> {\n            db.delete(\"scope\", \"mid = ?\", new String[]{String.valueOf(mid)});\n            for (Application app : scopes) {\n                if (app.packageName.equals(\"system\") && app.userId != 0) continue;\n                ContentValues values = new ContentValues();\n                values.put(\"mid\", mid);\n                values.put(\"app_pkg_name\", app.packageName);\n                values.put(\"user_id\", app.userId);\n                db.insertWithOnConflict(\"scope\", null, values, SQLiteDatabase.CONFLICT_IGNORE);\n            }\n        });\n        // Called by manager, should be async\n        updateCaches(false);\n        return true;\n    }\n\n    public boolean setModuleScope(String packageName, String scopePackageName, int userId) {\n        if (scopePackageName == null) return false;\n        int mid = getModuleId(packageName);\n        if (mid == -1) return false;\n        if (scopePackageName.equals(\"system\") && userId != 0) return false;\n        executeInTransaction(() -> {\n            ContentValues values = new ContentValues();\n            values.put(\"mid\", mid);\n            values.put(\"app_pkg_name\", scopePackageName);\n            values.put(\"user_id\", userId);\n            db.insertWithOnConflict(\"scope\", null, values, SQLiteDatabase.CONFLICT_IGNORE);\n        });\n        // Called by xposed service, should be async\n        updateCaches(false);\n        return true;\n    }\n\n    public boolean removeModuleScope(String packageName, String scopePackageName, int userId) {\n        if (scopePackageName == null) return false;\n        int mid = getModuleId(packageName);\n        if (mid == -1) return false;\n        if (scopePackageName.equals(\"system\") && userId != 0) return false;\n        executeInTransaction(() -> {\n            db.delete(\"scope\", \"mid = ? AND app_pkg_name = ? AND user_id = ?\", new String[]{String.valueOf(mid), scopePackageName, String.valueOf(userId)});\n        });\n        // Called by xposed service, should be async\n        updateCaches(false);\n        return true;\n    }\n\n\n    public String[] enabledModules() {\n        return listModules(\"enabled\");\n    }\n\n    public boolean removeModule(String packageName) {\n        if (removeModuleWithoutCache(packageName)) {\n            // called by oneway binder\n            // Called only when the application is completely uninstalled\n            // If it's a module we need to return as soon as possible to broadcast to the manager\n            // for updating the module status\n            updateCaches(false);\n            return true;\n        }\n        return false;\n    }\n\n    private boolean removeModuleWithoutCache(String packageName) {\n        if (packageName.equals(\"lspd\")) return false;\n        boolean res = executeInTransaction(() -> db.delete(\"modules\", \"module_pkg_name = ?\", new String[]{packageName}) > 0);\n        try {\n            for (var user : UserService.getUsers()) {\n                removeModulePrefs(user.id, packageName);\n            }\n        } catch (Throwable e) {\n            Log.w(TAG, \"remove module prefs for \" + packageName);\n        }\n        return res;\n    }\n\n    private boolean removeModuleScopeWithoutCache(Application module) {\n        if (module.packageName.equals(\"lspd\")) return false;\n        int mid = getModuleId(module.packageName);\n        if (mid == -1) return false;\n        boolean res = executeInTransaction(() -> db.delete(\"scope\", \"mid = ? and user_id = ?\", new String[]{String.valueOf(mid), String.valueOf(module.userId)}) > 0);\n        try {\n            removeModulePrefs(module.userId, module.packageName);\n        } catch (IOException e) {\n            Log.w(TAG, \"removeModulePrefs\", e);\n        }\n        return res;\n    }\n\n    private boolean removeAppWithoutCache(Application app) {\n        return executeInTransaction(() -> db.delete(\"scope\", \"app_pkg_name = ? AND user_id=?\",\n                new String[]{app.packageName, String.valueOf(app.userId)}) > 0);\n    }\n\n    public boolean disableModule(String packageName) {\n        if (packageName.equals(\"lspd\")) return false;\n        boolean changed = executeInTransaction(() -> {\n            ContentValues values = new ContentValues();\n            values.put(\"enabled\", 0);\n            return db.update(\"modules\", values, \"module_pkg_name = ?\", new String[]{packageName}) > 0;\n        });\n        if (changed) {\n            // called by manager, should be async\n            updateCaches(false);\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    public boolean enableModule(String packageName) throws RemoteException {\n        if (packageName.equals(\"lspd\")) return false;\n        PackageInfo pkgInfo = PackageService.getPackageInfoFromAllUsers(packageName, PackageService.MATCH_ALL_FLAGS).values().stream().findFirst().orElse(null);\n        if (pkgInfo == null || pkgInfo.applicationInfo == null) return false;\n        var modulePath = getModuleApkPath(pkgInfo.applicationInfo);\n        if (modulePath == null) return false;\n        boolean changed = updateModuleApkPath(packageName, modulePath, false);\n        changed = executeInTransaction(() -> {\n            ContentValues values = new ContentValues();\n            values.put(\"enabled\", 1);\n            return db.update(\"modules\", values, \"module_pkg_name = ?\", new String[]{packageName}) > 0;\n        }) || changed;\n        if (changed) {\n            // Called by manager, should be async\n            updateCaches(false);\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    public void updateCache() {\n        // Called by oneway binder\n        updateCaches(true);\n    }\n\n    public void updateAppCache() {\n        // Called by oneway binder\n        cacheScopes();\n    }\n\n    public void setVerboseLog(boolean on) {\n        if (BuildConfig.DEBUG) return;\n        var logcatService = ServiceManager.getLogcatService();\n        if (on) {\n            logcatService.startVerbose();\n        } else {\n            logcatService.stopVerbose();\n        }\n        updateModulePrefs(\"lspd\", 0, \"config\", \"enable_verbose_log\", on);\n        verboseLog = on;\n    }\n\n    public boolean verboseLog() {\n        return BuildConfig.DEBUG || verboseLog;\n    }\n\n    public void setLogWatchdog(boolean on) {\n        var logcatService = ServiceManager.getLogcatService();\n        if (on) {\n            logcatService.enableWatchdog();\n        } else {\n            logcatService.disableWatchdog();\n        }\n        updateModulePrefs(\"lspd\", 0, \"config\", \"enable_log_watchdog\", on);\n        logWatchdog = on;\n    }\n\n    public boolean isLogWatchdogEnabled() {\n        return logWatchdog;\n    }\n\n    public void setDexObfuscate(boolean on) {\n        updateModulePrefs(\"lspd\", 0, \"config\", \"enable_dex_obfuscate\", on);\n    }\n\n    public boolean scopeRequestBlocked(String packageName) {\n        return scopeRequestBlocked.contains(packageName);\n    }\n\n    public void blockScopeRequest(String packageName) {\n        var set = new HashSet<>(scopeRequestBlocked);\n        set.add(packageName);\n        updateModulePrefs(\"lspd\", 0, \"config\", \"scope_request_blocked\", set);\n        scopeRequestBlocked = set;\n    }\n\n    public void removeBlockedScopeRequest(String packageName) {\n        var set = new HashSet<>(scopeRequestBlocked);\n        set.remove(packageName);\n        updateModulePrefs(\"lspd\", 0, \"config\", \"scope_request_blocked\", set);\n        scopeRequestBlocked = set;\n    }\n\n    // this is for manager and should not use the cache result\n    boolean dexObfuscate() {\n        var bool = getModulePrefs(\"lspd\", 0, \"config\").get(\"enable_dex_obfuscate\");\n        return bool == null || (boolean) bool;\n    }\n\n    public boolean enableStatusNotification() {\n        Log.d(TAG, \"show status notification = \" + enableStatusNotification);\n        return enableStatusNotification;\n    }\n\n    public void setEnableStatusNotification(boolean enable) {\n        updateModulePrefs(\"lspd\", 0, \"config\", \"enable_status_notification\", enable);\n        enableStatusNotification = enable;\n    }\n\n    public ParcelFileDescriptor getManagerApk() {\n        try {\n            return ConfigFileManager.getManagerApk();\n        } catch (Throwable e) {\n            Log.e(TAG, \"failed to open manager apk\", e);\n            return null;\n        }\n    }\n\n    public ParcelFileDescriptor getModulesLog() {\n        try {\n            var modulesLog = ServiceManager.getLogcatService().getModulesLog();\n            if (modulesLog == null) return null;\n            return ParcelFileDescriptor.open(modulesLog, ParcelFileDescriptor.MODE_READ_ONLY);\n        } catch (IOException e) {\n            Log.e(TAG, Log.getStackTraceString(e));\n            return null;\n        }\n    }\n\n    public ParcelFileDescriptor getVerboseLog() {\n        try {\n            var verboseLog = ServiceManager.getLogcatService().getVerboseLog();\n            if (verboseLog == null) return null;\n            return ParcelFileDescriptor.open(verboseLog, ParcelFileDescriptor.MODE_READ_ONLY);\n        } catch (FileNotFoundException e) {\n            Log.e(TAG, Log.getStackTraceString(e));\n            return null;\n        }\n    }\n\n    public boolean clearLogs(boolean verbose) {\n        ServiceManager.getLogcatService().refresh(verbose);\n        return true;\n    }\n\n    public boolean isManager(int uid) {\n        return uid == managerUid;\n    }\n\n    public boolean isManagerInstalled() {\n        return managerUid != -1;\n    }\n\n    public String getPrefsPath(String packageName, int uid) {\n        int userId = uid / PER_USER_RANGE;\n        var path = miscPath.resolve(\"prefs\" + (userId == 0 ? \"\" : String.valueOf(userId))).resolve(packageName);\n        var module = cachedModule.getOrDefault(packageName, null);\n        if (module != null && module.appId == uid % PER_USER_RANGE) {\n            try {\n                var perms = PosixFilePermissions.fromString(\"rwx--x--x\");\n                Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms));\n                walkFileTree(path, p -> {\n                    try {\n                        Os.chown(p.toString(), uid, uid);\n                    } catch (ErrnoException e) {\n                        Log.e(TAG, Log.getStackTraceString(e));\n                    }\n                });\n            } catch (IOException e) {\n                Log.e(TAG, Log.getStackTraceString(e));\n            }\n        }\n        return path.toString();\n    }\n\n    // this is slow, avoid using it\n    public Module getModule(int uid) {\n        for (var module : cachedModule.values()) {\n            if (module.appId == uid % PER_USER_RANGE) return module;\n        }\n        return null;\n    }\n\n    private void walkFileTree(Path rootDir, Consumer<Path> action) throws IOException {\n        if (Files.notExists(rootDir)) return;\n        Files.walkFileTree(rootDir, new SimpleFileVisitor<>() {\n            @Override\n            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {\n                action.accept(dir);\n                return FileVisitResult.CONTINUE;\n            }\n\n            @Override\n            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {\n                action.accept(file);\n                return FileVisitResult.CONTINUE;\n            }\n        });\n    }\n\n    private void removeModulePrefs(int uid, String packageName) throws IOException {\n        if (packageName == null) return;\n        var path = Paths.get(getPrefsPath(packageName, uid));\n        ConfigFileManager.deleteFolderIfExists(path);\n    }\n\n    public List<String> getDenyListPackages() {\n        List<String> result = new ArrayList<>();\n        if (!getApi().equals(\"Zygisk\")) return result;\n        if (!ConfigFileManager.magiskDbPath.exists()) return result;\n        try (final SQLiteDatabase magiskDb =\n                     SQLiteDatabase.openDatabase(ConfigFileManager.magiskDbPath, new SQLiteDatabase.OpenParams.Builder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build())) {\n            try (Cursor cursor = magiskDb.query(\"settings\", new String[]{\"value\"}, \"`key`=?\", new String[]{\"denylist\"}, null, null, null)) {\n                if (!cursor.moveToNext()) return result;\n                int valueIndex = cursor.getColumnIndex(\"value\");\n                if (valueIndex >= 0 && cursor.getInt(valueIndex) == 0) return result;\n            }\n            try (Cursor cursor = magiskDb.query(true, \"denylist\", new String[]{\"package_name\"}, null, null, null, null, null, null, null)) {\n                if (cursor == null) return result;\n                int packageNameIdx = cursor.getColumnIndex(\"package_name\");\n                while (cursor.moveToNext()) {\n                    result.add(cursor.getString(packageNameIdx));\n                }\n                return result;\n            }\n        } catch (Throwable e) {\n            Log.e(TAG, \"get denylist\", e);\n        }\n        return result;\n    }\n\n    public void setApi(String api) {\n        this.api = api;\n    }\n\n    public String getApi() {\n        return api;\n    }\n\n    public void exportScopes(ZipOutputStream os) throws IOException {\n        os.putNextEntry(new ZipEntry(\"scopes.txt\"));\n        cachedScope.forEach((scope, modules) -> {\n            try {\n                os.write((scope.processName + \"/\" + scope.uid + \"\\n\").getBytes(StandardCharsets.UTF_8));\n                for (var module : modules) {\n                    os.write((\"\\t\" + module.packageName + \"\\n\").getBytes(StandardCharsets.UTF_8));\n                    for (var cn : module.file.moduleClassNames) {\n                        os.write((\"\\t\\t\" + cn + \"\\n\").getBytes(StandardCharsets.UTF_8));\n                    }\n                    for (var ln : module.file.moduleLibraryNames) {\n                        os.write((\"\\t\\t\" + ln + \"\\n\").getBytes(StandardCharsets.UTF_8));\n                    }\n                }\n            } catch (IOException e) {\n                Log.w(TAG, scope.processName, e);\n            }\n        });\n        os.closeEntry();\n    }\n\n    synchronized SharedMemory getPreloadDex() {\n        return ConfigFileManager.getPreloadDex(dexObfuscate);\n    }\n\n    public boolean getAutoInclude(String packageName) {\n        try (Cursor cursor = db.query(\"modules\", new String[]{\"auto_include\"},\n               \"module_pkg_name = ? and auto_include = 1\", new String[]{packageName}, null, null, null, null)) {\n            return cursor == null || cursor.moveToNext();\n        }\n    }\n\n    public boolean setAutoInclude(String packageName, boolean enable) {\n        boolean changed = executeInTransaction(() -> {\n            ContentValues values = new ContentValues();\n            values.put(\"auto_include\", enable ? 1 : 0);\n            return db.update(\"modules\", values, \"module_pkg_name = ?\", new String[]{packageName}) > 0;\n        });\n        return true;\n    }\n\n    public String[] getAutoIncludeModules() {\n        return listModules(\"auto_include\");\n    }\n\n    private String[] listModules(String column) {\n        try (Cursor cursor = db.query(\"modules\", new String[]{\"module_pkg_name\"}, column + \" = 1\", null, null, null, null)) {\n            if (cursor == null) {\n                Log.e(TAG, \"query \" + column + \" modules failed\");\n                return null;\n            }\n            int modulePkgNameIdx = cursor.getColumnIndex(\"module_pkg_name\");\n            HashSet<String> result = new HashSet<>();\n            while (cursor.moveToNext()) {\n                var pkgName = cursor.getString(modulePkgNameIdx);\n                if (pkgName.equals(\"lspd\")) continue;\n                result.add(pkgName);\n            }\n            return result.toArray(new String[0]);\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2022 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.ILSPManagerService.DEX2OAT_CRASHED;\nimport static org.lsposed.lspd.ILSPManagerService.DEX2OAT_MOUNT_FAILED;\nimport static org.lsposed.lspd.ILSPManagerService.DEX2OAT_OK;\nimport static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SELINUX_PERMISSIVE;\nimport static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT;\n\nimport android.net.LocalServerSocket;\nimport android.os.Build;\nimport android.os.FileObserver;\nimport android.os.Process;\nimport android.os.SELinux;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.system.OsConstants;\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\n\nimport java.io.File;\nimport java.io.FileDescriptor;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\n\n@RequiresApi(Build.VERSION_CODES.Q)\npublic class Dex2OatService implements Runnable {\n    private static final String TAG = \"LSPosedDex2Oat\";\n    private static final String WRAPPER32 = \"bin/dex2oat32\";\n    private static final String WRAPPER64 = \"bin/dex2oat64\";\n    private static final String HOOKER32 = \"bin/liboat_hook32.so\";\n    private static final String HOOKER64 = \"bin/liboat_hook64.so\";\n\n    private final String[] dex2oatArray = new String[6];\n    private final FileDescriptor[] fdArray = new FileDescriptor[6];\n    private final FileObserver selinuxObserver;\n    private int compatibility = DEX2OAT_OK;\n\n    private void openDex2oat(int id, String path) {\n        try {\n            var fd = Os.open(path, OsConstants.O_RDONLY, 0);\n            dex2oatArray[id] = path;\n            fdArray[id] = fd;\n        } catch (ErrnoException ignored) {\n        }\n    }\n\n    /**\n     * Checks the ELF header of the target file.\n     * If 32-bit -> Assigns to Index 0 (Release) or 1 (Debug).\n     * If 64-bit -> Assigns to Index 2 (Release) or 3 (Debug).\n     */\n    private void checkAndAddDex2Oat(String path) {\n        if (path == null)\n            return;\n        File file = new File(path);\n        if (!file.exists())\n            return;\n\n        try (FileInputStream fis = new FileInputStream(file)) {\n            byte[] header = new byte[5];\n            if (fis.read(header) != 5)\n                return;\n\n            // 1. Verify ELF Magic: 0x7F 'E' 'L' 'F'\n            if (header[0] != 0x7F || header[1] != 'E' || header[2] != 'L' || header[3] != 'F') {\n                return;\n            }\n\n            // 2. Check Architecture (header[4]): 1 = 32-bit, 2 = 64-bit\n            boolean is32Bit = (header[4] == 1);\n            boolean is64Bit = (header[4] == 2);\n            boolean isDebug = path.contains(\"dex2oatd\");\n\n            int index = -1;\n\n            if (is32Bit) {\n                index = isDebug ? 1 : 0; // Index 0/1 maps to r32/d32 in C++\n            } else if (is64Bit) {\n                index = isDebug ? 3 : 2; // Index 2/3 maps to r64/d64 in C++\n            }\n\n            // 3. Assign to the detected slot\n            if (index != -1 && dex2oatArray[index] == null) {\n                dex2oatArray[index] = path;\n                try {\n                    // Open the FD for the wrapper to use later\n                    fdArray[index] = Os.open(path, OsConstants.O_RDONLY, 0);\n                    Log.i(TAG, \"Detected \" + path + \" as \" + (is64Bit ? \"64-bit\" : \"32-bit\") + \" -> Assigned Index \"\n                            + index);\n                } catch (ErrnoException e) {\n                    Log.e(TAG, \"Failed to open FD for \" + path, e);\n                    dex2oatArray[index] = null;\n                }\n            }\n        } catch (IOException e) {\n            // File not readable, skip\n        }\n    }\n\n    public Dex2OatService() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Android 10: Check the standard path.\n            // Logic will detect if it is 32-bit and put it in Index 0.\n            checkAndAddDex2Oat(\"/apex/com.android.runtime/bin/dex2oat\");\n            checkAndAddDex2Oat(\"/apex/com.android.runtime/bin/dex2oatd\");\n\n            // Check for explicit 64-bit paths (just in case)\n            checkAndAddDex2Oat(\"/apex/com.android.runtime/bin/dex2oat64\");\n            checkAndAddDex2Oat(\"/apex/com.android.runtime/bin/dex2oatd64\");\n        } else {\n            checkAndAddDex2Oat(\"/apex/com.android.art/bin/dex2oat32\");\n            checkAndAddDex2Oat(\"/apex/com.android.art/bin/dex2oatd32\");\n            checkAndAddDex2Oat(\"/apex/com.android.art/bin/dex2oat64\");\n            checkAndAddDex2Oat(\"/apex/com.android.art/bin/dex2oatd64\");\n        }\n\n        openDex2oat(4, \"/data/adb/modules/zygisk_vector/bin/liboat_hook32.so\");\n        openDex2oat(5, \"/data/adb/modules/zygisk_vector/bin/liboat_hook64.so\");\n\n        var enforce = Paths.get(\"/sys/fs/selinux/enforce\");\n        var policy = Paths.get(\"/sys/fs/selinux/policy\");\n        var list = new ArrayList<File>();\n        list.add(enforce.toFile());\n        list.add(policy.toFile());\n        selinuxObserver = new FileObserver(list, FileObserver.CLOSE_WRITE) {\n            @Override\n            public synchronized void onEvent(int i, @Nullable String s) {\n                Log.d(TAG, \"SELinux status changed\");\n                if (compatibility == DEX2OAT_CRASHED) {\n                    stopWatching();\n                    return;\n                }\n\n                boolean enforcing = false;\n                try (var is = Files.newInputStream(enforce)) {\n                    enforcing = is.read() == '1';\n                } catch (IOException ignored) {\n                }\n\n                if (!enforcing) {\n                    if (compatibility == DEX2OAT_OK) doMount(false);\n                    compatibility = DEX2OAT_SELINUX_PERMISSIVE;\n                } else if (SELinux.checkSELinuxAccess(\"u:r:untrusted_app:s0\",\n                        \"u:object_r:dex2oat_exec:s0\", \"file\", \"execute\")\n                        || SELinux.checkSELinuxAccess(\"u:r:untrusted_app:s0\",\n                        \"u:object_r:dex2oat_exec:s0\", \"file\", \"execute_no_trans\")) {\n                    if (compatibility == DEX2OAT_OK) doMount(false);\n                    compatibility = DEX2OAT_SEPOLICY_INCORRECT;\n                } else if (compatibility != DEX2OAT_OK) {\n                    doMount(true);\n                    if (notMounted()) {\n                        doMount(false);\n                        compatibility = DEX2OAT_MOUNT_FAILED;\n                        stopWatching();\n                    } else {\n                        compatibility = DEX2OAT_OK;\n                    }\n                }\n            }\n\n            @Override\n            public void stopWatching() {\n                super.stopWatching();\n                Log.w(TAG, \"SELinux observer stopped\");\n            }\n        };\n    }\n\n    private boolean notMounted() {\n        for (int i = 0; i < dex2oatArray.length && i < 4; i++) {\n            var bin = dex2oatArray[i];\n            if (bin == null) continue;\n            try {\n                var apex = Os.stat(\"/proc/1/root\" + bin);\n                var wrapper = Os.stat(i < 2 ? WRAPPER32 : WRAPPER64);\n                if (apex.st_dev != wrapper.st_dev || apex.st_ino != wrapper.st_ino) {\n                    Log.w(TAG, \"Check mount failed for \" + bin);\n                    return true;\n                }\n            } catch (ErrnoException e) {\n                Log.e(TAG, \"Check mount failed for \" + bin, e);\n                return true;\n            }\n        }\n        Log.d(TAG, \"Check mount succeeded\");\n        return false;\n    }\n\n    private void doMount(boolean enabled) {\n        doMountNative(enabled, dex2oatArray[0], dex2oatArray[1], dex2oatArray[2], dex2oatArray[3]);\n    }\n\n    public void start() {\n        if (notMounted()) { // Already mounted when restart daemon\n            doMount(true);\n            if (notMounted()) {\n                doMount(false);\n                compatibility = DEX2OAT_MOUNT_FAILED;\n                return;\n            }\n        }\n\n        var thread = new Thread(this);\n        thread.setName(\"dex2oat\");\n        thread.start();\n        selinuxObserver.startWatching();\n        selinuxObserver.onEvent(0, null);\n    }\n\n    @Override\n    public void run() {\n        Log.i(TAG, \"Dex2oat wrapper daemon start\");\n        var sockPath = getSockPath();\n        Log.d(TAG, \"wrapper path: \" + sockPath);\n        var xposed_file = \"u:object_r:xposed_file:s0\";\n        var dex2oat_exec = \"u:object_r:dex2oat_exec:s0\";\n        if (SELinux.checkSELinuxAccess(\"u:r:dex2oat:s0\", dex2oat_exec,\n                \"file\", \"execute_no_trans\")) {\n            SELinux.setFileContext(WRAPPER32, dex2oat_exec);\n            SELinux.setFileContext(WRAPPER64, dex2oat_exec);\n            setSockCreateContext(\"u:r:dex2oat:s0\");\n        } else {\n            SELinux.setFileContext(WRAPPER32, xposed_file);\n            SELinux.setFileContext(WRAPPER64, xposed_file);\n            setSockCreateContext(\"u:r:installd:s0\");\n        }\n        SELinux.setFileContext(HOOKER32, xposed_file);\n        SELinux.setFileContext(HOOKER64, xposed_file);\n        try (var server = new LocalServerSocket(sockPath)) {\n            setSockCreateContext(null);\n            while (true) {\n                try (var client = server.accept();\n                     var is = client.getInputStream();\n                     var os = client.getOutputStream()) {\n                    var id = is.read();\n                    var fd = new FileDescriptor[]{fdArray[id]};\n                    client.setFileDescriptorsForSend(fd);\n                    os.write(1);\n                    Log.d(TAG, \"Sent fd of \" + dex2oatArray[id]);\n                }\n            }\n        } catch (IOException e) {\n            Log.e(TAG, \"Dex2oat wrapper daemon crashed\", e);\n            if (compatibility == DEX2OAT_OK) {\n                doMount(false);\n                compatibility = DEX2OAT_CRASHED;\n            }\n        }\n    }\n\n    public int getCompatibility() {\n        return compatibility;\n    }\n\n    private native void doMountNative(boolean enabled,\n                                      String r32, String d32, String r64, String d64);\n\n    private static native boolean setSockCreateContext(String context);\n\n    private native String getSockPath();\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 - 2022 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.os.IBinder;\nimport android.os.Parcel;\nimport android.os.ParcelFileDescriptor;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.lspd.models.Module;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\npublic class LSPApplicationService extends ILSPApplicationService.Stub {\n    final static int DEX_TRANSACTION_CODE = ('_' << 24) | ('D' << 16) | ('E' << 8) | 'X';\n    final static int OBFUSCATION_MAP_TRANSACTION_CODE = ('_' << 24) | ('O' << 16) | ('B' << 8) | 'F';\n    // key: <uid, pid>\n    private final static Map<Pair<Integer, Integer>, ProcessInfo> processes = new ConcurrentHashMap<>();\n\n    static class ProcessInfo implements DeathRecipient {\n        final int uid;\n        final int pid;\n        final String processName;\n        final IBinder heartBeat;\n\n        ProcessInfo(int uid, int pid, String processName, IBinder heartBeat) throws RemoteException {\n            this.uid = uid;\n            this.pid = pid;\n            this.processName = processName;\n            this.heartBeat = heartBeat;\n            heartBeat.linkToDeath(this, 0);\n            Log.d(TAG, \"register \" + this);\n            processes.put(new Pair<>(uid, pid), this);\n        }\n\n        @Override\n        public void binderDied() {\n            Log.d(TAG, this + \" is dead\");\n            heartBeat.unlinkToDeath(this, 0);\n            processes.remove(new Pair<>(uid, pid), this);\n        }\n\n        @NonNull\n        @Override\n        public String toString() {\n            return \"ProcessInfo{\" +\n                    \"uid=\" + uid +\n                    \", pid=\" + pid +\n                    \", processName='\" + processName + '\\'' +\n                    \", heartBeat=\" + heartBeat +\n                    '}';\n        }\n    }\n\n    @Override\n    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        Log.d(TAG, \"LSPApplicationService.onTransact: code=\" + code);\n        switch (code) {\n            case DEX_TRANSACTION_CODE: {\n                var shm = ConfigManager.getInstance().getPreloadDex();\n                if (shm == null) return false;\n                reply.writeNoException();\n                // assume that write only a fd\n                shm.writeToParcel(reply, 0);\n                reply.writeLong(shm.getSize());\n                return true;\n            }\n            case OBFUSCATION_MAP_TRANSACTION_CODE: {\n                var obfuscation = ConfigManager.getInstance().dexObfuscate();\n                var signatures = ObfuscationManager.getSignatures();\n                reply.writeNoException();\n                reply.writeInt(signatures.size() * 2);\n                for (Map.Entry<String, String> entry : signatures.entrySet()) {\n                    reply.writeString(entry.getKey());\n                    // return val = key if obfuscation disabled\n                    reply.writeString(obfuscation ? entry.getValue() : entry.getKey());\n                }\n                return true;\n            }\n        }\n        return super.onTransact(code, data, reply, flags);\n    }\n\n    public boolean registerHeartBeat(int uid, int pid, String processName, IBinder heartBeat) {\n        try {\n            new ProcessInfo(uid, pid, processName, heartBeat);\n            return true;\n        } catch (RemoteException e) {\n            return false;\n        }\n    }\n\n    private List<Module> getAllModulesList() throws RemoteException {\n        var processInfo = ensureRegistered();\n        if (processInfo.uid == Process.SYSTEM_UID && processInfo.processName.equals(\"system\")) {\n            return ConfigManager.getInstance().getModulesForSystemServer();\n        }\n        if (ServiceManager.getManagerService().isRunningManager(processInfo.pid, processInfo.uid))\n            return Collections.emptyList();\n        return ConfigManager.getInstance().getModulesForProcess(processInfo.processName, processInfo.uid);\n    }\n\n    @Override\n    public boolean isLogMuted() throws RemoteException {\n        return !ServiceManager.getManagerService().isVerboseLog();\n    }\n\n    @Override\n    public List<Module> getLegacyModulesList() throws RemoteException {\n        return getAllModulesList().stream().filter(m -> m.file.legacy).collect(Collectors.toList());\n    }\n\n    @Override\n    public List<Module> getModulesList() throws RemoteException {\n        return getAllModulesList().stream().filter(m -> !m.file.legacy).collect(Collectors.toList());\n    }\n\n    @Override\n    public String getPrefsPath(String packageName) throws RemoteException {\n        ensureRegistered();\n        return ConfigManager.getInstance().getPrefsPath(packageName, getCallingUid());\n    }\n\n    @Override\n    public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) throws RemoteException {\n        var processInfo = ensureRegistered();\n        if (ServiceManager.getManagerService().postStartManager(processInfo.pid, processInfo.uid) ||\n                ConfigManager.getInstance().isManager(processInfo.uid)) {\n            binder.add(ServiceManager.getManagerService().obtainManagerBinder(processInfo.heartBeat, processInfo.pid, processInfo.uid));\n        }\n        return ConfigManager.getInstance().getManagerApk();\n    }\n\n    public boolean hasRegister(int uid, int pid) {\n        return processes.containsKey(new Pair<>(uid, pid));\n    }\n\n    @NonNull\n    private ProcessInfo ensureRegistered() throws RemoteException {\n        var uid = getCallingUid();\n        var pid = getCallingPid();\n        var key = new Pair<>(uid, pid);\n        ProcessInfo processInfo = processes.getOrDefault(key, null);\n        if (processInfo == null || uid != processInfo.uid || pid != processInfo.pid) {\n            processes.remove(key, processInfo);\n            Log.w(TAG, \"non-authorized: info=\" + processInfo + \" uid=\" + uid + \" pid=\" + pid);\n            throw new RemoteException(\"Not registered\");\n        }\n        return processInfo;\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java",
    "content": "package org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.LSPModuleService.FILES_DIR;\nimport static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;\n\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.ParcelFileDescriptor;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport org.lsposed.lspd.models.Module;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport io.github.libxposed.service.IXposedService;\n\npublic class LSPInjectedModuleService extends ILSPInjectedModuleService.Stub {\n\n    private static final String TAG = \"LSPosedInjectedModuleService\";\n\n    private final String mPackageName;\n\n    Map<String, Set<IRemotePreferenceCallback>> callbacks = new ConcurrentHashMap<>();\n\n    LSPInjectedModuleService(String packageName) {\n        mPackageName = packageName;\n    }\n\n    @Override\n    public int getFrameworkPrivilege() {\n        return IXposedService.FRAMEWORK_PRIVILEGE_ROOT;\n    }\n\n    @Override\n    public Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback) {\n        var bundle = new Bundle();\n        var userId = Binder.getCallingUid() / PER_USER_RANGE;\n        bundle.putSerializable(\"map\", ConfigManager.getInstance().getModulePrefs(mPackageName, userId, group));\n        if (callback != null) {\n            var groupCallbacks = callbacks.computeIfAbsent(group, k -> ConcurrentHashMap.newKeySet());\n            groupCallbacks.add(callback);\n            try {\n                callback.asBinder().linkToDeath(() -> groupCallbacks.remove(callback), 0);\n            } catch (RemoteException e) {\n                Log.w(TAG, \"requestRemotePreferences: \", e);\n            }\n        }\n        return bundle;\n    }\n\n    @Override\n    public ParcelFileDescriptor openRemoteFile(String path) throws RemoteException {\n        ConfigFileManager.ensureModuleFilePath(path);\n        var userId = Binder.getCallingUid() / PER_USER_RANGE;\n        try {\n            var dir = ConfigFileManager.resolveModuleDir(mPackageName, FILES_DIR, userId, -1);\n            return ParcelFileDescriptor.open(dir.resolve(path).toFile(), ParcelFileDescriptor.MODE_READ_ONLY);\n        } catch (Throwable e) {\n            throw new RemoteException(e.getMessage());\n        }\n    }\n\n    @Override\n    public String[] getRemoteFileList() throws RemoteException {\n        var userId = Binder.getCallingUid() / PER_USER_RANGE;\n        try {\n            var dir = ConfigFileManager.resolveModuleDir(mPackageName, FILES_DIR, userId, -1);\n            var files = dir.toFile().list();\n            return files == null ? new String[0] : files;\n\n        } catch (Throwable e) {\n            throw new RemoteException(e.getMessage());\n        }\n    }\n\n    void onUpdateRemotePreferences(String group, Bundle diff) {\n        var groupCallbacks = callbacks.get(group);\n        if (groupCallbacks != null) {\n            for (var callback : groupCallbacks) {\n                try {\n                    callback.onUpdate(diff);\n                } catch (RemoteException e) {\n                    groupCallbacks.remove(callback);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static android.content.Context.BIND_AUTO_CREATE;\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.annotation.SuppressLint;\nimport android.app.IServiceConnection;\nimport android.content.AttributionSource;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.VersionedPackage;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.ParcelFileDescriptor;\nimport android.os.RemoteException;\nimport android.os.SELinux;\nimport android.os.SystemProperties;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.util.Log;\nimport android.view.IWindowManager;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.daemon.BuildConfig;\nimport org.lsposed.lspd.ILSPManagerService;\nimport org.lsposed.lspd.models.Application;\nimport org.lsposed.lspd.models.UserInfo;\nimport org.lsposed.lspd.util.Utils;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport hidden.HiddenApiBridge;\nimport io.github.libxposed.service.IXposedService;\nimport rikka.parcelablelist.ParcelableListSlice;\n\npublic class LSPManagerService extends ILSPManagerService.Stub {\n\n    private static Intent managerIntent = null;\n    private boolean enabled = true;\n\n    public class ManagerGuard implements IBinder.DeathRecipient {\n        private final @NonNull\n        IBinder binder;\n        private final int pid;\n        private final int uid;\n        private final IServiceConnection connection = new IServiceConnection.Stub() {\n            @Override\n            public void connected(ComponentName name, IBinder service, boolean dead) {\n            }\n        };\n\n        public ManagerGuard(@NonNull IBinder binder, int pid, int uid) {\n            guard = this;\n            this.pid = pid;\n            this.uid = uid;\n            this.binder = binder;\n            try {\n                this.binder.linkToDeath(this, 0);\n                if (Utils.isMIUI) {\n                    var intent = new Intent();\n                    intent.setComponent(ComponentName.unflattenFromString(\"com.miui.securitycore/com.miui.xspace.service.XSpaceService\"));\n                    ActivityManagerService.bindService(intent, intent.getType(), connection, BIND_AUTO_CREATE, \"android\", 0);\n                }\n            } catch (Throwable e) {\n                Log.e(TAG, \"manager guard\", e);\n                guard = null;\n            }\n        }\n\n        @Override\n        public void binderDied() {\n            try {\n                binder.unlinkToDeath(this, 0);\n                ActivityManagerService.unbindService(connection);\n            } catch (Throwable e) {\n                Log.e(TAG, \"manager guard\", e);\n            }\n            guard = null;\n        }\n\n        boolean isAlive() {\n            return binder.isBinderAlive();\n        }\n    }\n\n    public ManagerGuard guard = null;\n\n    // guard to determine the manager or the injected app\n    // that is to say, to make the parasitic success,\n    // we should make sure no extra launch after parasitic\n    // launch is queued and before the process is started\n    private boolean pendingManager = false;\n    private int managerPid = -1;\n\n    LSPManagerService() {\n    }\n\n    private static Intent getManagerIntent() {\n        if (managerIntent != null) return managerIntent;\n        try {\n            var intent = PackageService.getLaunchIntentForPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);\n            if (intent == null) {\n                var pkgInfo = PackageService.getPackageInfo(BuildConfig.MANAGER_INJECTED_PKG_NAME, PackageManager.GET_ACTIVITIES, 0);\n                if (pkgInfo != null && pkgInfo.activities != null && pkgInfo.activities.length > 0) {\n                    for (var activityInfo : pkgInfo.activities) {\n                        if (activityInfo.processName.equals(activityInfo.packageName)) {\n                            intent = new Intent();\n                            intent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));\n                            intent.setAction(Intent.ACTION_MAIN);\n                            break;\n                        }\n                    }\n                }\n            }\n            if (intent != null) {\n                if (intent.getCategories() != null) intent.getCategories().clear();\n                intent.addCategory(\"org.lsposed.manager.LAUNCH_MANAGER\");\n                intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);\n                managerIntent = new Intent(intent);\n            }\n        } catch (RemoteException e) {\n            Log.e(TAG, \"get Intent\", e);\n        }\n        return managerIntent;\n    }\n\n    static void openManager(Uri withData) {\n        var intent = getManagerIntent();\n        if (intent == null) return;\n        intent = new Intent(intent);\n        intent.setData(withData);\n        try {\n            ActivityManagerService.startActivityAsUserWithFeature(\"android\", null, intent, intent.getType(), null, null, 0, 0, null, null, 0);\n        } catch (RemoteException e) {\n            Log.e(TAG, \"failed to open manager\");\n        }\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    public static void broadcastIntent(Intent inIntent) {\n        var intent = new Intent(\"org.lsposed.manager.NOTIFICATION\");\n        intent.putExtra(Intent.EXTRA_INTENT, inIntent);\n        intent.addFlags(0x01000000); //Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND\n        intent.addFlags(0x00400000); //Intent.FLAG_RECEIVER_FROM_SHELL\n        intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);\n        try {\n            ActivityManagerService.broadcastIntentWithFeature(null, intent,\n                    null, null, 0, null, null,\n                    null, -1, null, true, false,\n                    0);\n            intent.setPackage(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME);\n            ActivityManagerService.broadcastIntentWithFeature(null, intent,\n                    null, null, 0, null, null,\n                    null, -1, null, true, false,\n                    0);\n        } catch (RemoteException t) {\n            Log.e(TAG, \"Broadcast to manager failed: \", t);\n        }\n    }\n\n    private void ensureWebViewPermission(File f) {\n        if (!f.exists()) return;\n        SELinux.setFileContext(f.getAbsolutePath(), \"u:object_r:xposed_file:s0\");\n        try {\n            Os.chown(f.getAbsolutePath(), BuildConfig.MANAGER_INJECTED_UID, BuildConfig.MANAGER_INJECTED_UID);\n        } catch (ErrnoException e) {\n            Log.e(TAG, \"chown of webview\", e);\n        }\n        if (f.isDirectory()) {\n            for (var g : f.listFiles()) {\n                ensureWebViewPermission(g);\n            }\n        }\n    }\n\n    private void ensureWebViewPermission() {\n        try {\n            var pkgInfo = PackageService.getPackageInfo(BuildConfig.MANAGER_INJECTED_PKG_NAME, 0, 0);\n            if (pkgInfo != null) {\n                var cacheDir = new File(HiddenApiBridge.ApplicationInfo_credentialProtectedDataDir(pkgInfo.applicationInfo) + \"/cache\");\n                // The cache directory does not exist after `pm clear`\n                cacheDir.mkdirs();\n                ensureWebViewPermission(cacheDir);\n            }\n        } catch (Throwable e) {\n            Log.w(TAG, \"cannot ensure webview dir\", e);\n        }\n    }\n\n    synchronized boolean preStartManager() {\n        pendingManager = true;\n        managerPid = -1;\n        return true;\n    }\n\n    // return true to inject manager\n    synchronized boolean shouldStartManager(int pid, int uid, String processName) {\n        if (!enabled || uid != BuildConfig.MANAGER_INJECTED_UID || !BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME.equals(processName) || !pendingManager)\n            return false;\n        pendingManager = false;\n        managerPid = pid;\n        Log.d(TAG, \"starting injected manager: pid = \" + pid + \" uid = \" + uid + \" processName = \" + processName);\n        return true;\n    }\n\n    synchronized boolean setEnabled(boolean newValue) {\n        enabled = newValue;\n        Log.i(TAG, \"manager enabled = \" + enabled);\n        return enabled;\n    }\n\n    // return true to send manager binder\n    boolean postStartManager(int pid, int uid) {\n        return enabled && uid == BuildConfig.MANAGER_INJECTED_UID && pid == managerPid;\n    }\n\n    public @NonNull\n    IBinder obtainManagerBinder(@NonNull IBinder heartbeat, int pid, int uid) {\n        new ManagerGuard(heartbeat, pid, uid);\n        if (uid == BuildConfig.MANAGER_INJECTED_UID)\n            ensureWebViewPermission();\n        return this;\n    }\n\n    public boolean isRunningManager(int pid, int uid) {\n        return false;\n    }\n\n    void onSystemServerDied() {\n        guard = null;\n    }\n\n    @Override\n    public IBinder asBinder() {\n        return this;\n    }\n\n    @Override\n    public int getXposedApiVersion() {\n        return IXposedService.API;\n    }\n\n    @Override\n    public int getXposedVersionCode() {\n        return BuildConfig.VERSION_CODE;\n    }\n\n    @Override\n    public String getXposedVersionName() {\n        return BuildConfig.VERSION_NAME;\n    }\n\n    @Override\n    public String getApi() {\n        return ConfigManager.getInstance().getApi();\n    }\n\n    @Override\n    public ParcelableListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags, boolean filterNoProcess) throws RemoteException {\n        return PackageService.getInstalledPackagesFromAllUsers(flags, filterNoProcess);\n    }\n\n    @Override\n    public String[] enabledModules() {\n        return ConfigManager.getInstance().enabledModules();\n    }\n\n    @Override\n    public boolean enableModule(String packageName) throws RemoteException {\n        return ConfigManager.getInstance().enableModule(packageName);\n    }\n\n    @Override\n    public boolean setModuleScope(String packageName, List<Application> scope) throws RemoteException {\n        return ConfigManager.getInstance().setModuleScope(packageName, scope);\n    }\n\n    @Override\n    public List<Application> getModuleScope(String packageName) {\n        return ConfigManager.getInstance().getModuleScope(packageName);\n    }\n\n    @Override\n    public boolean disableModule(String packageName) {\n        return ConfigManager.getInstance().disableModule(packageName);\n    }\n\n    @Override\n    public boolean isVerboseLog() {\n        return ConfigManager.getInstance().verboseLog();\n    }\n\n    @Override\n    public void setVerboseLog(boolean enabled) {\n        ConfigManager.getInstance().setVerboseLog(enabled);\n    }\n\n    @Override\n    public ParcelFileDescriptor getVerboseLog() {\n        return ConfigManager.getInstance().getVerboseLog();\n    }\n\n    @Override\n    public ParcelFileDescriptor getModulesLog() {\n        ServiceManager.getLogcatService().checkLogFile();\n        return ConfigManager.getInstance().getModulesLog();\n    }\n\n    @Override\n    public boolean clearLogs(boolean verbose) {\n        return ConfigManager.getInstance().clearLogs(verbose);\n    }\n\n    @Override\n    public PackageInfo getPackageInfo(String packageName, int flags, int uid) throws RemoteException {\n        return PackageService.getPackageInfo(packageName, flags, uid);\n    }\n\n    @Override\n    public void forceStopPackage(String packageName, int userId) throws RemoteException {\n        ActivityManagerService.forceStopPackage(packageName, userId);\n    }\n\n    @Override\n    public void reboot() throws RemoteException {\n        PowerService.reboot(false, null, false);\n    }\n\n    @Override\n    public boolean uninstallPackage(String packageName, int userId) throws RemoteException {\n        try {\n            if (ActivityManagerService.startUserInBackground(userId)) {\n                var pkg = new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST);\n                return PackageService.uninstallPackage(pkg, userId);\n            } else {\n                return false;\n            }\n        } catch (InterruptedException | ReflectiveOperationException e) {\n            Log.e(TAG, e.getMessage(), e);\n            return false;\n        }\n    }\n\n    @Override\n    public boolean isSepolicyLoaded() {\n        return SELinux.checkSELinuxAccess(\"u:r:dex2oat:s0\", \"u:object_r:dex2oat_exec:s0\",\n                \"file\", \"execute_no_trans\");\n    }\n\n    @Override\n    public List<UserInfo> getUsers() throws RemoteException {\n        var users = new LinkedList<UserInfo>();\n        for (var user : UserService.getUsers()) {\n            var info = new UserInfo();\n            info.id = user.id;\n            info.name = user.name;\n            users.add(info);\n        }\n        return users;\n    }\n\n    @Override\n    public int installExistingPackageAsUser(String packageName, int userId) {\n        try {\n            if (ActivityManagerService.startUserInBackground(userId))\n                return PackageService.installExistingPackageAsUser(packageName, userId);\n            else return PackageService.INSTALL_FAILED_INTERNAL_ERROR;\n        } catch (Throwable e) {\n            Log.w(TAG, \"install existing package as user: \", e);\n            return PackageService.INSTALL_FAILED_INTERNAL_ERROR;\n        }\n    }\n\n    @Override\n    public boolean systemServerRequested() {\n        return ServiceManager.systemServerRequested();\n    }\n\n    @Override\n    public int startActivityAsUserWithFeature(Intent intent, int userId) throws RemoteException {\n        if (!intent.getBooleanExtra(\"lsp_no_switch_to_user\", false)) {\n            intent.removeExtra(\"lsp_no_switch_to_user\");\n            var currentUser = ActivityManagerService.getCurrentUser();\n            if (currentUser == null) return -1;\n            var parent = UserService.getProfileParent(userId);\n            if (parent < 0) return -1;\n            if (currentUser.id != parent) {\n                if (!ActivityManagerService.switchUser(parent)) return -1;\n                var window = android.os.ServiceManager.getService(Context.WINDOW_SERVICE);\n                if (window != null) {\n                    var wm = IWindowManager.Stub.asInterface(window);\n                    wm.lockNow(null);\n                }\n            }\n        }\n        return ActivityManagerService.startActivityAsUserWithFeature(\"android\", null, intent, intent.getType(), null, null, 0, 0, null, null, userId);\n    }\n\n    @Override\n    public ParcelableListSlice<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) throws RemoteException {\n        return PackageService.queryIntentActivities(intent, intent.getType(), flags, userId);\n    }\n\n    @Override\n    public boolean dex2oatFlagsLoaded() {\n        return SystemProperties.get(\"dalvik.vm.dex2oat-flags\").contains(\"--inline-max-code-units=0\");\n    }\n\n    @Override\n    public void setHiddenIcon(boolean hide) {\n        Bundle args = new Bundle();\n        args.putString(\"value\", hide ? \"0\" : \"1\");\n        args.putString(\"_user\", \"0\");\n        try {\n            var contentProvider = ActivityManagerService.getContentProvider(\"settings\", 0);\n            if (contentProvider != null) {\n                try {\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n                        contentProvider.call(new AttributionSource.Builder(1000).setPackageName(\"android\").build(),\n                                \"settings\", \"PUT_global\", \"show_hidden_icon_apps_enabled\", args);\n                    } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n                        contentProvider.call(\"android\", null, \"settings\", \"PUT_global\", \"show_hidden_icon_apps_enabled\", args);\n                    } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n                        contentProvider.call(\"android\", \"settings\", \"PUT_global\", \"show_hidden_icon_apps_enabled\", args);\n                    }\n                } catch (NoSuchMethodError e) {\n                    Log.w(TAG, \"setHiddenIcon: \", e);\n                }\n            }\n        } catch (Throwable e) {\n            Log.w(TAG, \"setHiddenIcon: \", e);\n        }\n    }\n\n    @Override\n    public void getLogs(ParcelFileDescriptor zipFd) {\n        ConfigFileManager.getLogs(zipFd);\n    }\n\n    @Override\n    public void restartFor(Intent intent) throws RemoteException {\n    }\n\n    @Override\n    public List<String> getDenyListPackages() {\n        return ConfigManager.getInstance().getDenyListPackages();\n    }\n\n    @Override\n    public void flashZip(String zipPath, ParcelFileDescriptor outputStream) {\n        var processBuilder = new ProcessBuilder(\"magisk\", \"--install-module\", zipPath);\n        var fd = new File(\"/proc/self/fd/\" + outputStream.getFd());\n        processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(fd));\n        try (outputStream; var fdw = new FileOutputStream(fd, true)) {\n            var proc = processBuilder.start();\n            if (proc.waitFor(10, TimeUnit.SECONDS)) {\n                var exit = proc.exitValue();\n                if (exit == 0) {\n                    fdw.write(\"- Reboot after 5s\\n\".getBytes());\n                    Thread.sleep(5000);\n                    reboot();\n                } else {\n                    var s = \"! Flash failed, exit with \" + exit + \"\\n\";\n                    fdw.write(s.getBytes());\n                }\n            } else {\n                proc.destroy();\n                fdw.write(\"! Timeout, abort\\n\".getBytes());\n            }\n        } catch (IOException | InterruptedException | RemoteException e) {\n            Log.e(TAG, \"flashZip: \", e);\n        }\n    }\n\n    @Override\n    public void clearApplicationProfileData(String packageName) throws RemoteException {\n        PackageService.clearApplicationProfileData(packageName);\n    }\n\n    @Override\n    public boolean enableStatusNotification() {\n        return ConfigManager.getInstance().enableStatusNotification();\n    }\n\n    @Override\n    public void setEnableStatusNotification(boolean enable) {\n        ConfigManager.getInstance().setEnableStatusNotification(enable);\n        if (enable) {\n            LSPNotificationManager.notifyStatusNotification();\n        } else {\n            LSPNotificationManager.cancelStatusNotification();\n        }\n    }\n\n    @Override\n    public boolean performDexOptMode(String packageName) throws RemoteException {\n        return PackageService.performDexOptMode(packageName);\n    }\n\n    @Override\n    public boolean getDexObfuscate() {\n        return ConfigManager.getInstance().dexObfuscate();\n    }\n\n    @Override\n    public void setDexObfuscate(boolean enabled) {\n        ConfigManager.getInstance().setDexObfuscate(enabled);\n    }\n\n    @Override\n    public int getDex2OatWrapperCompatibility() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            return ServiceManager.getDex2OatService().getCompatibility();\n        } else {\n            return 0;\n        }\n    }\n\n    @Override\n    public void setLogWatchdog(boolean enabled) {\n        ConfigManager.getInstance().setLogWatchdog(enabled);\n    }\n\n    @Override\n    public boolean isLogWatchdogEnabled() {\n        return ConfigManager.getInstance().isLogWatchdogEnabled();\n    }\n\n    @Override\n    public boolean setAutoInclude(String packageName, boolean enabled) {\n        return ConfigManager.getInstance().setAutoInclude(packageName, enabled);\n    }\n\n    @Override\n    public boolean getAutoInclude(String packageName) {\n        return ConfigManager.getInstance().getAutoInclude(packageName);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;\n\nimport android.content.AttributionSource;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.ParcelFileDescriptor;\nimport android.os.RemoteException;\nimport android.util.ArrayMap;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport org.lsposed.daemon.BuildConfig;\nimport org.lsposed.lspd.models.Module;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.WeakHashMap;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport io.github.libxposed.service.IXposedScopeCallback;\nimport io.github.libxposed.service.IXposedService;\n\npublic class LSPModuleService extends IXposedService.Stub {\n\n    private final static String TAG = \"LSPosedModuleService\";\n\n    private final static Set<Integer> uidSet = ConcurrentHashMap.newKeySet();\n    private final static Map<Module, LSPModuleService> serviceMap = Collections.synchronizedMap(new WeakHashMap<>());\n\n    public final static String FILES_DIR = \"files\";\n\n    private final @NonNull\n    Module loadedModule;\n\n    static void uidClear() {\n        uidSet.clear();\n    }\n\n    static void uidStarts(int uid) {\n        if (!uidSet.contains(uid)) {\n            uidSet.add(uid);\n            var module = ConfigManager.getInstance().getModule(uid);\n            if (module != null && module.file != null && !module.file.legacy) {\n                var service = serviceMap.computeIfAbsent(module, LSPModuleService::new);\n                service.sendBinder(uid);\n            }\n        }\n    }\n\n    static void uidGone(int uid) {\n        uidSet.remove(uid);\n    }\n\n    private void sendBinder(int uid) {\n        var name = loadedModule.packageName;\n        try {\n            int userId = uid / PackageService.PER_USER_RANGE;\n            var authority = name + AUTHORITY_SUFFIX;\n            var provider = ActivityManagerService.getContentProvider(authority, userId);\n            if (provider == null) {\n                Log.d(TAG, \"no service provider for \" + name);\n                return;\n            }\n            var extra = new Bundle();\n            extra.putBinder(\"binder\", asBinder());\n            Bundle reply = null;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n                reply = provider.call(new AttributionSource.Builder(1000).setPackageName(\"android\").build(), authority, SEND_BINDER, null, extra);\n            } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {\n                reply = provider.call(\"android\", null, authority, SEND_BINDER, null, extra);\n            } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n                reply = provider.call(\"android\", authority, SEND_BINDER, null, extra);\n            } else {\n                reply = provider.call(\"android\", SEND_BINDER, null, extra);\n            }\n            if (reply != null) {\n                Log.d(TAG, \"sent module binder to \" + name);\n            } else {\n                Log.w(TAG, \"failed to send module binder to \" + name);\n            }\n        } catch (Throwable e) {\n            Log.w(TAG, \"failed to send module binder for uid \" + uid, e);\n        }\n    }\n\n    LSPModuleService(@NonNull Module module) {\n        loadedModule = module;\n    }\n\n    private int ensureModule() throws RemoteException {\n        var appId = Binder.getCallingUid() % PER_USER_RANGE;\n        if (loadedModule.appId != appId) {\n            throw new RemoteException(\"Module \" + loadedModule.packageName + \" is not for uid \" + Binder.getCallingUid());\n        }\n        return Binder.getCallingUid() / PER_USER_RANGE;\n    }\n\n    @Override\n    public int getAPIVersion() throws RemoteException {\n        ensureModule();\n        return API;\n    }\n\n    @Override\n    public String getFrameworkName() throws RemoteException {\n        ensureModule();\n        return \"LSPosed\";\n    }\n\n    @Override\n    public String getFrameworkVersion() throws RemoteException {\n        ensureModule();\n        return BuildConfig.VERSION_NAME;\n    }\n\n    @Override\n    public long getFrameworkVersionCode() throws RemoteException {\n        ensureModule();\n        return BuildConfig.VERSION_CODE;\n    }\n\n    @Override\n    public int getFrameworkPrivilege() throws RemoteException {\n        ensureModule();\n        return IXposedService.FRAMEWORK_PRIVILEGE_ROOT;\n    }\n\n    @Override\n    public List<String> getScope() throws RemoteException {\n        ensureModule();\n        ArrayList<String> res = new ArrayList<>();\n        var scope = ConfigManager.getInstance().getModuleScope(loadedModule.packageName);\n        if (scope == null) return res;\n        for (var s : scope) {\n            res.add(s.packageName);\n        }\n        return res;\n    }\n\n    @Override\n    public void requestScope(String packageName, IXposedScopeCallback callback) throws RemoteException {\n        var userId = ensureModule();\n        if (ConfigManager.getInstance().scopeRequestBlocked(loadedModule.packageName)) {\n            callback.onScopeRequestDenied(packageName);\n        } else {\n            LSPNotificationManager.requestModuleScope(loadedModule.packageName, userId, packageName, callback);\n            callback.onScopeRequestPrompted(packageName);\n        }\n    }\n\n    @Override\n    public String removeScope(String packageName) throws RemoteException {\n        var userId = ensureModule();\n        try {\n            if (!ConfigManager.getInstance().removeModuleScope(loadedModule.packageName, packageName, userId)) {\n                return \"Invalid request\";\n            }\n            return null;\n        } catch (Throwable e) {\n            return e.getMessage();\n        }\n    }\n\n    @Override\n    public Bundle requestRemotePreferences(String group) throws RemoteException {\n        var userId = ensureModule();\n        var bundle = new Bundle();\n        bundle.putSerializable(\"map\", ConfigManager.getInstance().getModulePrefs(loadedModule.packageName, userId, group));\n        return bundle;\n    }\n\n    @Override\n    public void updateRemotePreferences(String group, Bundle diff) throws RemoteException {\n        var userId = ensureModule();\n        Map<String, Object> values = new ArrayMap<>();\n        if (diff.containsKey(\"delete\")) {\n            var deletes = (Set<?>) diff.getSerializable(\"delete\");\n            for (var key : deletes) {\n                values.put((String) key, null);\n            }\n        }\n        if (diff.containsKey(\"put\")) {\n            try {\n                var puts = (Map<?, ?>) diff.getSerializable(\"put\");\n                for (var entry : puts.entrySet()) {\n                    values.put((String) entry.getKey(), entry.getValue());\n                }\n            } catch (Throwable e) {\n                Log.e(TAG, \"updateRemotePreferences: \", e);\n            }\n        }\n        try {\n            ConfigManager.getInstance().updateModulePrefs(loadedModule.packageName, userId, group, values);\n            ((LSPInjectedModuleService) loadedModule.service).onUpdateRemotePreferences(group, diff);\n        } catch (Throwable e) {\n            throw new RemoteException(e.getMessage());\n        }\n    }\n\n    @Override\n    public void deleteRemotePreferences(String group) throws RemoteException {\n        var userId = ensureModule();\n        ConfigManager.getInstance().deleteModulePrefs(loadedModule.packageName, userId, group);\n    }\n\n    @Override\n    public String[] listRemoteFiles() throws RemoteException {\n        var userId = ensureModule();\n        try {\n            var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, FILES_DIR, userId, Binder.getCallingUid());\n            var files = dir.toFile().list();\n            return files == null ? new String[0] : files;\n        } catch (IOException e) {\n            throw new RemoteException(e.getMessage());\n        }\n    }\n\n    @Override\n    public ParcelFileDescriptor openRemoteFile(String path) throws RemoteException {\n        var userId = ensureModule();\n        ConfigFileManager.ensureModuleFilePath(path);\n        try {\n            var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, FILES_DIR, userId, Binder.getCallingUid());\n            return ParcelFileDescriptor.open(dir.resolve(path).toFile(), ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_READ_WRITE);\n        } catch (IOException e) {\n            throw new RemoteException(e.getMessage());\n        }\n    }\n\n    @Override\n    public boolean deleteRemoteFile(String path) throws RemoteException {\n        var userId = ensureModule();\n        ConfigFileManager.ensureModuleFilePath(path);\n        try {\n            var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, FILES_DIR, userId, Binder.getCallingUid());\n            return dir.resolve(path).toFile().delete();\n        } catch (IOException e) {\n            throw new RemoteException(e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPNotificationManager.java",
    "content": "package org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.app.INotificationManager;\nimport android.app.Notification;\nimport android.app.NotificationChannel;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ParceledListSlice;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.AdaptiveIconDrawable;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.Icon;\nimport android.graphics.drawable.LayerDrawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport org.lsposed.daemon.R;\nimport org.lsposed.lspd.util.FakeContext;\n\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport io.github.libxposed.service.IXposedScopeCallback;\n\npublic class LSPNotificationManager {\n    static final String UPDATED_CHANNEL_ID = \"lsposed_module_updated\";\n    static final String SCOPE_CHANNEL_ID = \"lsposed_module_scope\";\n    private static final String STATUS_CHANNEL_ID = \"lsposed_status\";\n    private static final int STATUS_NOTIFICATION_ID = 2000;\n    private static final String opPkg = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ?\n            \"android\" : \"com.android.settings\";\n\n    private static final Map<String, Integer> notificationIds = new ConcurrentHashMap<>();\n    private static int previousNotificationId = STATUS_NOTIFICATION_ID;\n\n    static final String openManagerAction = UUID.randomUUID().toString();\n    static final String moduleScope = UUID.randomUUID().toString();\n\n    private static INotificationManager notificationManager = null;\n    private static IBinder binder = null;\n\n    private static final IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() {\n        @Override\n        public void binderDied() {\n            Log.w(TAG, \"notificationManager is dead\");\n            binder.unlinkToDeath(this, 0);\n            binder = null;\n            notificationManager = null;\n        }\n    };\n\n    private static INotificationManager getNotificationManager() throws RemoteException {\n        if (binder == null || notificationManager == null) {\n            binder = android.os.ServiceManager.getService(Context.NOTIFICATION_SERVICE);\n            binder.linkToDeath(recipient, 0);\n            notificationManager = INotificationManager.Stub.asInterface(binder);\n        }\n        return notificationManager;\n    }\n\n    private static Bitmap getBitmap(int id) {\n        var r = ConfigFileManager.getResources();\n        var res = r.getDrawable(id, r.newTheme());\n        if (res instanceof BitmapDrawable) {\n            return ((BitmapDrawable) res).getBitmap();\n        } else {\n            if (res instanceof AdaptiveIconDrawable) {\n                var layers = new Drawable[]{((AdaptiveIconDrawable) res).getBackground(),\n                        ((AdaptiveIconDrawable) res).getForeground()};\n                res = new LayerDrawable(layers);\n            }\n            var bitmap = Bitmap.createBitmap(res.getIntrinsicWidth(),\n                    res.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);\n            var canvas = new Canvas(bitmap);\n            res.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n            res.draw(canvas);\n            return bitmap;\n        }\n    }\n\n    private static Icon getNotificationIcon() {\n        return Icon.createWithBitmap(getBitmap(R.drawable.ic_notification));\n    }\n\n    private static boolean hasNotificationChannelForSystem(\n            INotificationManager nm, String channelId) throws RemoteException {\n        NotificationChannel channel;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            channel = nm.getNotificationChannelForPackage(\"android\", 1000, channelId, null, false);\n        } else {\n            channel = nm.getNotificationChannelForPackage(\"android\", 1000, channelId, false);\n        }\n        if (channel != null) {\n            Log.d(TAG, \"hasNotificationChannelForSystem: \" + channel);\n        }\n        return channel != null;\n    }\n\n    private static void createNotificationChannel(INotificationManager nm) throws RemoteException {\n        var context = new FakeContext();\n        var list = new ArrayList<NotificationChannel>();\n\n        var updated = new NotificationChannel(UPDATED_CHANNEL_ID,\n                context.getString(R.string.module_updated_channel_name),\n                NotificationManager.IMPORTANCE_HIGH);\n        updated.setShowBadge(false);\n        if (hasNotificationChannelForSystem(nm, UPDATED_CHANNEL_ID)) {\n            Log.d(TAG, \"update notification channel: \" + UPDATED_CHANNEL_ID);\n            nm.updateNotificationChannelForPackage(\"android\", 1000, updated);\n        } else {\n            list.add(updated);\n        }\n\n        var status = new NotificationChannel(STATUS_CHANNEL_ID,\n                context.getString(R.string.status_channel_name),\n                NotificationManager.IMPORTANCE_MIN);\n        status.setShowBadge(false);\n        if (hasNotificationChannelForSystem(nm, STATUS_CHANNEL_ID)) {\n            Log.d(TAG, \"update notification channel: \" + STATUS_CHANNEL_ID);\n            nm.updateNotificationChannelForPackage(\"android\", 1000, status);\n        } else {\n            list.add(status);\n        }\n\n        var scope = new NotificationChannel(SCOPE_CHANNEL_ID,\n                context.getString(R.string.scope_channel_name),\n                NotificationManager.IMPORTANCE_HIGH);\n        scope.setShowBadge(false);\n        if (hasNotificationChannelForSystem(nm, SCOPE_CHANNEL_ID)) {\n            Log.d(TAG, \"update notification channel: \" + SCOPE_CHANNEL_ID);\n            nm.updateNotificationChannelForPackage(\"android\", 1000, scope);\n        } else {\n            list.add(scope);\n        }\n\n        Log.d(TAG, \"create notification channels for android: \" + list);\n        nm.createNotificationChannelsForPackage(\"android\", 1000, new ParceledListSlice<>(list));\n    }\n\n    static void notifyStatusNotification() {\n        var intent = new Intent(openManagerAction);\n        intent.setPackage(\"android\");\n        var context = new FakeContext();\n        int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;\n        var notification = new Notification.Builder(context, STATUS_CHANNEL_ID)\n                .setContentTitle(context.getString(R.string.lsposed_running_notification_title))\n                .setContentText(context.getString(R.string.lsposed_running_notification_content))\n                .setSmallIcon(getNotificationIcon())\n                .setContentIntent(PendingIntent.getBroadcast(context, 1, intent, flags))\n                .setVisibility(Notification.VISIBILITY_SECRET)\n                .setColor(0xFFF48FB1)\n                .setOngoing(true)\n                .setAutoCancel(false)\n                .build();\n        notification.extras.putString(\"android.substName\", \"LSPosed\");\n        try {\n            var nm = getNotificationManager();\n            createNotificationChannel(nm);\n            nm.enqueueNotificationWithTag(\"android\", opPkg, null,\n                    STATUS_NOTIFICATION_ID, notification, 0);\n        } catch (RemoteException e) {\n            Log.e(TAG, \"notifyStatusNotification: \", e);\n        }\n    }\n\n    static void cancelStatusNotification() {\n        try {\n            var nm = getNotificationManager();\n            createNotificationChannel(nm);\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                nm.cancelNotificationWithTag(\"android\", \"android\", null, STATUS_NOTIFICATION_ID, 0);\n            } else {\n                nm.cancelNotificationWithTag(\"android\", null, STATUS_NOTIFICATION_ID, 0);\n            }\n        } catch (RemoteException e) {\n            Log.e(TAG, \"cancelStatusNotification: \", e);\n        }\n    }\n\n    private static PendingIntent getModuleIntent(String modulePackageName, int moduleUserId) {\n        var intent = new Intent(openManagerAction);\n        intent.setPackage(\"android\");\n        intent.setData(new Uri.Builder().scheme(\"module\").encodedAuthority(modulePackageName + \":\" + moduleUserId).build());\n        int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;\n        return PendingIntent.getBroadcast(new FakeContext(), 3, intent, flags);\n    }\n\n    private static PendingIntent getModuleScopeIntent(String modulePackageName, int moduleUserId, String scopePackageName, String action, IXposedScopeCallback callback) {\n        var intent = new Intent(moduleScope);\n        intent.setPackage(\"android\");\n        intent.setData(new Uri.Builder().scheme(\"module\").encodedAuthority(modulePackageName + \":\" + moduleUserId).encodedPath(scopePackageName).appendQueryParameter(\"action\", action).build());\n        var extras = new Bundle();\n        extras.putBinder(\"callback\", callback.asBinder());\n        intent.putExtras(extras);\n        int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;\n        return PendingIntent.getBroadcast(new FakeContext(), 4, intent, flags);\n    }\n\n    private static String getNotificationIdKey(String channel, String modulePackageName, int moduleUserId) {\n        return channel + \"/\" + modulePackageName + \":\" + moduleUserId;\n    }\n\n    private static int pushAndGetNotificationId(String channel, String modulePackageName, int moduleUserId) {\n        var idKey = getNotificationIdKey(channel, modulePackageName, moduleUserId);\n        // previousNotificationId start with 2001\n        // https://android.googlesource.com/platform/frameworks/base/+/master/proto/src/system_messages.proto\n        // https://android.googlesource.com/platform/system/core/+/master/libcutils/include/private/android_filesystem_config.h\n        // (AID_APP_END - AID_APP_START) x10 = 100000 < NOTE_NETWORK_AVAILABLE\n        return notificationIds.computeIfAbsent(idKey, key -> previousNotificationId++);\n    }\n\n    static void notifyModuleUpdated(String modulePackageName,\n                                    int moduleUserId,\n                                    boolean enabled,\n                                    boolean systemModule) {\n        var context = new FakeContext();\n        var userName = UserService.getUserName(moduleUserId);\n        String title = context.getString(enabled ? systemModule ?\n                R.string.xposed_module_updated_notification_title_system :\n                R.string.xposed_module_updated_notification_title :\n                R.string.module_is_not_activated_yet);\n        String content = context.getString(enabled ? systemModule ?\n                R.string.xposed_module_updated_notification_content_system :\n                R.string.xposed_module_updated_notification_content :\n                (moduleUserId == 0 ?\n                        R.string.module_is_not_activated_yet_main_user_detailed :\n                        R.string.module_is_not_activated_yet_multi_user_detailed), modulePackageName, userName);\n\n        var style = new Notification.BigTextStyle();\n        style.bigText(content);\n\n        var notification = new Notification.Builder(context, UPDATED_CHANNEL_ID)\n                .setContentTitle(title)\n                .setContentText(content)\n                .setSmallIcon(getNotificationIcon())\n                .setContentIntent(getModuleIntent(modulePackageName, moduleUserId))\n                .setVisibility(Notification.VISIBILITY_SECRET)\n                .setColor(0xFFF48FB1)\n                .setAutoCancel(true)\n                .setStyle(style)\n                .build();\n        notification.extras.putString(\"android.substName\", \"LSPosed\");\n        try {\n            var nm = getNotificationManager();\n            nm.enqueueNotificationWithTag(\"android\", opPkg, modulePackageName,\n                    pushAndGetNotificationId(UPDATED_CHANNEL_ID, modulePackageName, moduleUserId),\n                    notification, 0);\n        } catch (RemoteException e) {\n            Log.e(TAG, \"notify module updated\", e);\n        }\n    }\n\n    static void requestModuleScope(String modulePackageName, int moduleUserId, String scopePackageName, IXposedScopeCallback callback) {\n        var context = new FakeContext();\n        var userName = UserService.getUserName(moduleUserId);\n        String title = context.getString(R.string.xposed_module_request_scope_title);\n        String content = context.getString(R.string.xposed_module_request_scope_content, modulePackageName, userName, scopePackageName);\n\n        var style = new Notification.BigTextStyle();\n        style.bigText(content);\n\n        var notification = new Notification.Builder(context, SCOPE_CHANNEL_ID)\n                .setContentTitle(title)\n                .setContentText(content)\n                .setSmallIcon(getNotificationIcon())\n                .setVisibility(Notification.VISIBILITY_SECRET)\n                .setColor(0xFFF48FB1)\n                .setAutoCancel(true)\n                .setTimeoutAfter(1000 * 60 * 60)\n                .setStyle(style)\n                .setDeleteIntent(getModuleScopeIntent(modulePackageName, moduleUserId, scopePackageName, \"delete\", callback))\n                .setActions(new Notification.Action.Builder(\n                                Icon.createWithResource(context, R.drawable.ic_baseline_check_24),\n                                context.getString(R.string.scope_approve),\n                                getModuleScopeIntent(modulePackageName, moduleUserId, scopePackageName, \"approve\", callback))\n                                .build(),\n                        new Notification.Action.Builder(\n                                Icon.createWithResource(context, R.drawable.ic_baseline_close_24),\n                                context.getString(R.string.scope_deny),\n                                getModuleScopeIntent(modulePackageName, moduleUserId, scopePackageName, \"deny\", callback))\n                                .build(),\n                        new Notification.Action.Builder(\n                                Icon.createWithResource(context, R.drawable.ic_baseline_block_24),\n                                context.getString(R.string.nerver_ask_again),\n                                getModuleScopeIntent(modulePackageName, moduleUserId, scopePackageName, \"block\", callback))\n                                .build()\n                ).build();\n        notification.extras.putString(\"android.substName\", \"LSPosed\");\n        try {\n            var nm = getNotificationManager();\n            nm.enqueueNotificationWithTag(\"android\", opPkg, modulePackageName,\n                    pushAndGetNotificationId(SCOPE_CHANNEL_ID, modulePackageName, moduleUserId),\n                    notification, 0);\n        } catch (RemoteException e) {\n            try {\n                callback.onScopeRequestFailed(scopePackageName, e.getMessage());\n            } catch (RemoteException ignored) {\n            }\n            Log.e(TAG, \"request module scope\", e);\n        }\n    }\n\n    static void cancelNotification(String channel, String modulePackageName, int moduleUserId) {\n        try {\n            var idKey = getNotificationIdKey(channel, modulePackageName, moduleUserId);\n            var idValue = notificationIds.get(idKey);\n            if (idValue == null) return;\n            var nm = getNotificationManager();\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                nm.cancelNotificationWithTag(\"android\", \"android\", modulePackageName, idValue, 0);\n            } else {\n                nm.cancelNotificationWithTag(\"android\", modulePackageName, idValue, 0);\n            }\n            notificationIds.remove(idKey);\n        } catch (RemoteException e) {\n            Log.e(TAG, \"cancel notification\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPSystemServerService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 - 2022 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\nimport static org.lsposed.lspd.service.ServiceManager.getSystemServiceManager;\n\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IServiceCallback;\nimport android.os.Parcel;\nimport android.os.RemoteException;\nimport android.os.SystemProperties;\nimport android.util.Log;\n\npublic class LSPSystemServerService extends ILSPSystemServerService.Stub implements IBinder.DeathRecipient {\n\n    private final String proxyServiceName;\n    private IBinder originService = null;\n    private int requested;\n\n    public boolean systemServerRequested() {\n        return requested > 0;\n    }\n\n    public void putBinderForSystemServer() {\n        android.os.ServiceManager.addService(proxyServiceName, this);\n        binderDied();\n    }\n\n    public LSPSystemServerService(int maxRetry, String serviceName) {\n        Log.d(TAG, \"LSPSystemServerService::LSPSystemServerService with proxy \" + serviceName);\n        proxyServiceName = serviceName;\n        requested = -maxRetry;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            // Registers a callback when system is registering an authentic \"serial\" service\n            // And we are proxying all requests to that system service\n            var serviceCallback = new IServiceCallback.Stub() {\n                @Override\n                public void onRegistration(String name, IBinder binder) {\n                    Log.d(TAG, \"LSPSystemServerService::LSPSystemServerService onRegistration: \" + name + \" \" + binder);\n                    if (name.equals(proxyServiceName) && binder != null && binder != LSPSystemServerService.this) {\n                        Log.d(TAG, \"Register \" + name + \" \" + binder);\n                        originService = binder;\n                        LSPSystemServerService.this.linkToDeath();\n                    }\n                }\n\n                @Override\n                public IBinder asBinder() {\n                    return this;\n                }\n            };\n            try {\n                getSystemServiceManager().registerForNotifications(proxyServiceName, serviceCallback);\n            } catch (Throwable e) {\n                Log.e(TAG, \"unregister: \", e);\n            }\n        }\n    }\n\n    @Override\n    public ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) {\n        Log.d(TAG, \"ILSPApplicationService.requestApplicationService: \" + uid + \" \" + pid + \" \" + processName + \" \" + heartBeat);\n        requested = 1;\n        if (ConfigManager.getInstance().shouldSkipSystemServer() || uid != 1000 || heartBeat == null || !\"system\".equals(processName))\n            return null;\n        else\n            return ServiceManager.requestApplicationService(uid, pid, processName, heartBeat);\n    }\n\n    @Override\n    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        Log.d(TAG, \"LSPSystemServerService.onTransact: code=\" + code);\n        if (originService != null) {\n            return originService.transact(code, data, reply, flags);\n        }\n\n        switch (code) {\n            case BridgeService.TRANSACTION_CODE -> {\n                int uid = data.readInt();\n                int pid = data.readInt();\n                String processName = data.readString();\n                IBinder heartBeat = data.readStrongBinder();\n                var service = requestApplicationService(uid, pid, processName, heartBeat);\n                if (service != null) {\n                    Log.d(TAG, \"LSPSystemServerService.onTransact requestApplicationService granted: \" + service);\n                    reply.writeNoException();\n                    reply.writeStrongBinder(service.asBinder());\n                    return true;\n                } else {\n                    Log.d(TAG, \"LSPSystemServerService.onTransact requestApplicationService rejected\");\n                    return false;\n                }\n            }\n            case LSPApplicationService.OBFUSCATION_MAP_TRANSACTION_CODE, LSPApplicationService.DEX_TRANSACTION_CODE -> {\n                // Proxy LSP dex transaction to Application Binder\n                return ServiceManager.getApplicationService().onTransact(code, data, reply, flags);\n            }\n            default -> {\n                return super.onTransact(code, data, reply, flags);\n            }\n        }\n    }\n\n    public void linkToDeath() {\n        try {\n            originService.linkToDeath(this, 0);\n        } catch (Throwable e) {\n            Log.e(TAG, \"system server service: link to death\", e);\n        }\n    }\n\n    @Override\n    public void binderDied() {\n        if (originService != null) {\n            originService.unlinkToDeath(this, 0);\n            originService = null;\n        }\n    }\n\n    public void maybeRetryInject() {\n        if (requested < 0) {\n            Log.w(TAG, \"System server injection fails, trying a restart\");\n            ++requested;\n            if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && Build.SUPPORTED_32_BIT_ABIS.length > 0) {\n                // Only devices with both 32-bit and 64-bit support have zygote_secondary\n                SystemProperties.set(\"ctl.restart\", \"zygote_secondary\");\n            } else {\n                SystemProperties.set(\"ctl.restart\", \"zygote\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LSPosedService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static android.content.Intent.EXTRA_UID;\nimport static org.lsposed.lspd.service.LSPNotificationManager.SCOPE_CHANNEL_ID;\nimport static org.lsposed.lspd.service.LSPNotificationManager.UPDATED_CHANNEL_ID;\nimport static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\nimport static org.lsposed.lspd.service.ServiceManager.getExecutorService;\n\nimport android.app.IApplicationThread;\nimport android.app.IUidObserver;\nimport android.content.Context;\nimport android.content.IIntentReceiver;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.provider.Telephony;\nimport android.telephony.TelephonyManager;\nimport android.util.Log;\n\nimport org.lsposed.daemon.BuildConfig;\nimport org.lsposed.lspd.models.Application;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.zip.ZipFile;\n\nimport hidden.HiddenApiBridge;\nimport io.github.libxposed.service.IXposedScopeCallback;\n\npublic class LSPosedService extends ILSPosedService.Stub {\n    private static final int AID_NOBODY = 9999;\n    private static final int USER_NULL = -10000;\n    private static final String ACTION_USER_ADDED = \"android.intent.action.USER_ADDED\";\n    public static final String ACTION_USER_REMOVED = \"android.intent.action.USER_REMOVED\";\n    private static final String EXTRA_USER_HANDLE = \"android.intent.extra.user_handle\";\n    private static final String EXTRA_REMOVED_FOR_ALL_USERS = \"android.intent.extra.REMOVED_FOR_ALL_USERS\";\n    private static boolean bootCompleted = false;\n    private IBinder appThread = null;\n\n    private static boolean isModernModules(ApplicationInfo info) {\n        String[] apks;\n        if (info.splitSourceDirs != null) {\n            apks = Arrays.copyOf(info.splitSourceDirs, info.splitSourceDirs.length + 1);\n            apks[info.splitSourceDirs.length] = info.sourceDir;\n        } else apks = new String[]{info.sourceDir};\n        for (var apk : apks) {\n            try (var zip = new ZipFile(apk)) {\n                if (zip.getEntry(\"META-INF/xposed/java_init.list\") != null) {\n                    return true;\n                }\n            } catch (IOException ignored) {\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) {\n        if (Binder.getCallingUid() != 1000) {\n            Log.w(TAG, \"Someone else got my binder!?\");\n            return null;\n        }\n        if (ServiceManager.getApplicationService().hasRegister(uid, pid)) {\n            Log.d(TAG, \"Skipped duplicated request for uid \" + uid + \" pid \" + pid);\n            return null;\n        }\n        if (!ServiceManager.getManagerService().shouldStartManager(pid, uid, processName) && ConfigManager.getInstance().shouldSkipProcess(new ConfigManager.ProcessScope(processName, uid))) {\n            Log.d(TAG, \"Skipped \" + processName + \"/\" + uid);\n            return null;\n        }\n        Log.d(TAG, \"returned service\");\n        return ServiceManager.requestApplicationService(uid, pid, processName, heartBeat);\n    }\n\n    /**\n     * This part is quite complex.\n     * For modules, we never care about its user id, we only care about its apk path.\n     * So we will only process module's removal when it's removed from all users.\n     * And FULLY_REMOVE is exactly the one.\n     * <p>\n     * For applications, we care about its user id.\n     * So we will process application's removal when it's removed from every single user.\n     * However, PACKAGE_REMOVED will be triggered by `pm hide`, so we use UID_REMOVED instead.\n     */\n\n    private void dispatchPackageChanged(Intent intent) {\n        if (intent == null) return;\n        int uid = intent.getIntExtra(EXTRA_UID, AID_NOBODY);\n        if (uid == AID_NOBODY || uid <= 0) return;\n        int userId = intent.getIntExtra(\"android.intent.extra.user_handle\", USER_NULL);\n        var intentAction = intent.getAction();\n        if (intentAction == null) return;\n        var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);\n        if (userId == USER_NULL) userId = uid % PER_USER_RANGE;\n        Uri uri = intent.getData();\n        var module = ConfigManager.getInstance().getModule(uid);\n        String moduleName = (uri != null) ? uri.getSchemeSpecificPart() : (module != null) ? module.packageName : null;\n\n        ApplicationInfo applicationInfo = null;\n        if (moduleName != null) {\n            try {\n                applicationInfo = PackageService.getApplicationInfo(moduleName, PackageManager.GET_META_DATA | PackageService.MATCH_ALL_FLAGS, 0);\n            } catch (Throwable ignored) {\n            }\n        }\n\n        boolean isXposedModule = applicationInfo != null && ((applicationInfo.metaData != null && applicationInfo.metaData.containsKey(\"xposedminversion\")) || isModernModules(applicationInfo));\n\n        switch (intentAction) {\n            case Intent.ACTION_PACKAGE_FULLY_REMOVED -> {\n                // for module, remove module\n                // because we only care about when the apk is gone\n                if (moduleName != null) {\n                    if (allUsers && ConfigManager.getInstance().removeModule(moduleName)) {\n                        isXposedModule = true;\n                        broadcastAndShowNotification(moduleName, userId, intent, true);\n                    }\n                    LSPNotificationManager.cancelNotification(UPDATED_CHANNEL_ID, moduleName, userId);\n                }\n            }\n            case Intent.ACTION_PACKAGE_REMOVED -> {\n                if (moduleName != null) {\n                    LSPNotificationManager.cancelNotification(UPDATED_CHANNEL_ID, moduleName, userId);\n                }\n                break;\n            }\n            case Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED -> {\n                var configManager = ConfigManager.getInstance();\n                // make sure that the change is for the complete package, not only a\n                // component\n                String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);\n                if (components != null && !Arrays.stream(components).reduce(false, (p, c) -> p || c.equals(moduleName), Boolean::logicalOr)) {\n                    return;\n                }\n                if (isXposedModule) {\n                    // When installing a new Xposed module, we update the apk path to mark it as a\n                    // module to send a broadcast when modules that have not been activated are\n                    // uninstalled.\n                    // If cache not updated, assume it's not xposed module\n                    isXposedModule = configManager.updateModuleApkPath(moduleName, ConfigManager.getInstance().getModuleApkPath(applicationInfo), false);\n                } else {\n                    if (configManager.isUidHooked(uid)) {\n                        // it will automatically remove obsolete app from database\n                        configManager.updateAppCache();\n                    }\n                    if (intentAction.equals(Intent.ACTION_PACKAGE_ADDED) && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {\n                        for (String xposedModule : configManager.getAutoIncludeModules()) {\n                            // For Xposed modules with auto_include set, we always add new applications\n                            // to its scope\n                            var list = configManager.getModuleScope(xposedModule);\n                            if (list != null) {\n                                Application scope = new Application();\n                                scope.packageName = moduleName;\n                                scope.userId = userId;\n                                list.add(scope);\n                                try {\n                                    if (!configManager.setModuleScope(xposedModule, list)) {\n                                        Log.e(TAG, \"failed to set scope for \" + xposedModule);\n                                    }\n                                } catch(RemoteException re) {\n                                    Log.e(TAG, \"failed to set scope for \" + xposedModule, re);\n                                }\n                            }\n                        }\n                    }\n                }\n                broadcastAndShowNotification(moduleName, userId, intent, isXposedModule);\n            }\n            case Intent.ACTION_UID_REMOVED -> {\n                // when a package is removed (rather than hide) for a single user\n                // (apk may still be there because of multi-user)\n                broadcastAndShowNotification(moduleName, userId, intent, isXposedModule);\n                if (isXposedModule) {\n                    // it will auto remove obsolete app and scope from database\n                    ConfigManager.getInstance().updateCache();\n                } else if (ConfigManager.getInstance().isUidHooked(uid)) {\n                    // it will auto remove obsolete scope from database\n                    ConfigManager.getInstance().updateAppCache();\n                }\n            }\n        }\n        boolean removed = Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(intentAction) || Intent.ACTION_UID_REMOVED.equals(intentAction);\n\n        Log.d(TAG, \"Package changed: uid=\" + uid + \" userId=\" + userId + \" action=\" + intentAction + \" isXposedModule=\" + isXposedModule + \" isAllUsers=\" + allUsers);\n\n        if (BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME.equals(moduleName) && userId == 0) {\n            Log.d(TAG, \"Manager updated\");\n            ConfigManager.getInstance().updateManager(removed);\n        }\n    }\n\n    private void broadcastAndShowNotification(String packageName, int userId, Intent intent, boolean isXposedModule) {\n        Log.d(TAG, \"package \" + packageName + \" changed, dispatching to manager\");\n        var action = intent.getAction();\n        var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);\n        intent.putExtra(\"android.intent.extra.PACKAGES\", packageName);\n        intent.putExtra(Intent.EXTRA_USER, userId);\n        intent.putExtra(\"isXposedModule\", isXposedModule);\n        LSPManagerService.broadcastIntent(intent);\n        if (isXposedModule) {\n            var enabledModules = ConfigManager.getInstance().enabledModules();\n            var scope = ConfigManager.getInstance().getModuleScope(packageName);\n            boolean systemModule = scope != null && scope.parallelStream().anyMatch(app -> app.packageName.equals(\"system\"));\n            boolean enabled = Arrays.asList(enabledModules).contains(packageName);\n            if (!(Intent.ACTION_UID_REMOVED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action) || allUsers))\n                LSPNotificationManager.notifyModuleUpdated(packageName, userId, enabled, systemModule);\n        }\n    }\n\n    private void dispatchUserChanged(Intent intent) {\n        if (intent == null) return;\n        int uid = intent.getIntExtra(EXTRA_USER_HANDLE, AID_NOBODY);\n        if (uid == AID_NOBODY || uid <= 0) return;\n        LSPManagerService.broadcastIntent(intent);\n    }\n\n    private void dispatchBootCompleted(Intent intent) {\n        bootCompleted = true;\n        var configManager = ConfigManager.getInstance();\n        if (configManager.enableStatusNotification()) {\n            LSPNotificationManager.notifyStatusNotification();\n        } else {\n            LSPNotificationManager.cancelStatusNotification();\n        }\n    }\n\n    private void dispatchConfigurationChanged(Intent intent) {\n        if (!bootCompleted) return;\n        ConfigFileManager.reloadConfiguration();\n        var configManager = ConfigManager.getInstance();\n        if (configManager.enableStatusNotification()) {\n            LSPNotificationManager.notifyStatusNotification();\n        } else {\n            LSPNotificationManager.cancelStatusNotification();\n        }\n    }\n\n    private void dispatchSecretCodeReceive(Intent i) {\n        LSPManagerService.openManager(null);\n    }\n\n    private void dispatchOpenManager(Intent intent) {\n        LSPManagerService.openManager(intent.getData());\n    }\n\n    private void dispatchModuleScope(Intent intent) {\n        Log.d(TAG, \"dispatchModuleScope: \" + intent);\n        var data = intent.getData();\n        var extras = intent.getExtras();\n        if (extras == null || data == null) return;\n        var callback = extras.getBinder(\"callback\");\n        if (callback == null || !callback.isBinderAlive()) return;\n        var authority = data.getEncodedAuthority();\n        if (authority == null) return;\n        var s = authority.split(\":\", 2);\n        if (s.length != 2) return;\n        var packageName = s[0];\n        int userId;\n        try {\n            userId = Integer.parseInt(s[1]);\n        } catch (NumberFormatException e) {\n            return;\n        }\n        var scopePackageName = data.getPath();\n        if (scopePackageName == null) return;\n        scopePackageName = scopePackageName.substring(1);\n        var action = data.getQueryParameter(\"action\");\n        if (action == null) return;\n\n        var iCallback = IXposedScopeCallback.Stub.asInterface(callback);\n        try {\n            var applicationInfo = PackageService.getApplicationInfo(scopePackageName, 0, userId);\n            if (applicationInfo == null) {\n                iCallback.onScopeRequestFailed(scopePackageName, \"Package not found\");\n                return;\n            }\n\n            switch (action) {\n                case \"approve\" -> {\n                    ConfigManager.getInstance().setModuleScope(packageName, scopePackageName, userId);\n                    iCallback.onScopeRequestApproved(scopePackageName);\n                }\n                case \"deny\" -> iCallback.onScopeRequestDenied(scopePackageName);\n                case \"delete\" -> iCallback.onScopeRequestTimeout(scopePackageName);\n                case \"block\" -> {\n                    ConfigManager.getInstance().blockScopeRequest(packageName);\n                    iCallback.onScopeRequestDenied(scopePackageName);\n                }\n            }\n            Log.i(TAG, action + \" scope \" + scopePackageName + \" for \" + packageName + \" in user \" + userId);\n        } catch (RemoteException e) {\n            try {\n                iCallback.onScopeRequestFailed(scopePackageName, e.getMessage());\n            } catch (RemoteException ignored) {\n                // callback died\n            }\n        }\n        LSPNotificationManager.cancelNotification(SCOPE_CHANNEL_ID, packageName, userId);\n    }\n\n    private void registerReceiver(List<IntentFilter> filters, String requiredPermission, int userId, Consumer<Intent> task, int flag) {\n        var receiver = new IIntentReceiver.Stub() {\n            @Override\n            public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {\n                getExecutorService().submit(() -> {\n                    try {\n                        task.accept(intent);\n                    } catch (Throwable t) {\n                        Log.e(TAG, \"performReceive: \", t);\n                    }\n                });\n                if (!ordered && !Objects.equals(intent.getAction(), Intent.ACTION_LOCKED_BOOT_COMPLETED))\n                    return;\n                try {\n                    ActivityManagerService.finishReceiver(this, appThread, resultCode, data, extras, false, intent.getFlags());\n                } catch (RemoteException e) {\n                    Log.e(TAG, \"finish receiver\", e);\n                }\n            }\n        };\n        try {\n            for (var filter : filters) {\n                ActivityManagerService.registerReceiver(\"android\", null, receiver, filter, requiredPermission, userId, flag);\n            }\n        } catch (RemoteException e) {\n            Log.e(TAG, \"register receiver\", e);\n        }\n    }\n\n    private void registerReceiver(List<IntentFilter> filters, int userId, Consumer<Intent> task) {\n        //noinspection InlinedApi\n        registerReceiver(filters, \"android.permission.BRICK\", userId, task, Context.RECEIVER_NOT_EXPORTED);\n    }\n\n    private void registerPackageReceiver() {\n        var packageFilter = new IntentFilter();\n        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);\n        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);\n        packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);\n        packageFilter.addDataScheme(\"package\");\n\n        var uidFilter = new IntentFilter(Intent.ACTION_UID_REMOVED);\n\n        registerReceiver(List.of(packageFilter, uidFilter), -1, this::dispatchPackageChanged);\n        Log.d(TAG, \"registered package receiver\");\n    }\n\n    private void registerConfigurationReceiver() {\n        var intentFilter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);\n\n        registerReceiver(List.of(intentFilter), 0, this::dispatchConfigurationChanged);\n        Log.d(TAG, \"registered configuration receiver\");\n    }\n\n    private void registerSecretCodeReceiver() {\n        IntentFilter intentFilter = new IntentFilter();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            intentFilter.addAction(TelephonyManager.ACTION_SECRET_CODE);\n        } else {\n            // noinspection InlinedApi\n            intentFilter.addAction(Telephony.Sms.Intents.SECRET_CODE_ACTION);\n        }\n        intentFilter.addDataAuthority(\"5776733\", null);\n        intentFilter.addDataScheme(\"android_secret_code\");\n\n        //noinspection InlinedApi\n        registerReceiver(List.of(intentFilter), \"android.permission.CONTROL_INCALL_EXPERIENCE\",\n                0, this::dispatchSecretCodeReceive, Context.RECEIVER_EXPORTED);\n        Log.d(TAG, \"registered secret code receiver\");\n    }\n\n    private void registerBootCompleteReceiver() {\n        var intentFilter = new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);\n        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);\n        registerReceiver(List.of(intentFilter), 0, this::dispatchBootCompleted);\n        Log.d(TAG, \"registered boot receiver\");\n    }\n\n    private void registerUserChangeReceiver() {\n        var userFilter = new IntentFilter();\n        userFilter.addAction(ACTION_USER_ADDED);\n        userFilter.addAction(ACTION_USER_REMOVED);\n\n        registerReceiver(List.of(userFilter), -1, this::dispatchUserChanged);\n        Log.d(TAG, \"registered user info change receiver\");\n    }\n\n    private void registerOpenManagerReceiver() {\n        var intentFilter = new IntentFilter(LSPNotificationManager.openManagerAction);\n        var moduleFilter = new IntentFilter(intentFilter);\n        moduleFilter.addDataScheme(\"module\");\n\n        registerReceiver(List.of(intentFilter, moduleFilter), 0, this::dispatchOpenManager);\n        Log.d(TAG, \"registered open manager receiver\");\n    }\n\n    private void registerModuleScopeReceiver() {\n        var intentFilter = new IntentFilter(LSPNotificationManager.moduleScope);\n        intentFilter.addDataScheme(\"module\");\n\n        registerReceiver(List.of(intentFilter), 0, this::dispatchModuleScope);\n        Log.d(TAG, \"registered module scope receiver\");\n    }\n\n    private void registerUidObserver() {\n        try {\n            var which = HiddenApiBridge.ActivityManager_UID_OBSERVER_ACTIVE()\n                    | HiddenApiBridge.ActivityManager_UID_OBSERVER_GONE()\n                    | HiddenApiBridge.ActivityManager_UID_OBSERVER_IDLE()\n                    | HiddenApiBridge.ActivityManager_UID_OBSERVER_CACHED();\n            LSPModuleService.uidClear();\n            ActivityManagerService.registerUidObserver(new IUidObserver.Stub() {\n                @Override\n                public void onUidActive(int uid) {\n                    LSPModuleService.uidStarts(uid);\n                }\n\n                @Override\n                public void onUidCachedChanged(int uid, boolean cached) {\n                    if (!cached) LSPModuleService.uidStarts(uid);\n                }\n\n                @Override\n                public void onUidIdle(int uid, boolean disabled) {\n                    LSPModuleService.uidStarts(uid);\n                }\n\n                @Override\n                public void onUidGone(int uid, boolean disabled) {\n                    LSPModuleService.uidGone(uid);\n                }\n            }, which, HiddenApiBridge.ActivityManager_PROCESS_STATE_UNKNOWN(), null);\n        } catch (RemoteException e) {\n            Log.e(TAG, \"registerUidObserver\", e);\n        }\n    }\n\n    @Override\n    public void dispatchSystemServerContext(IBinder appThread, IBinder activityToken, String api) {\n        Log.d(TAG, \"received system context\");\n        this.appThread = appThread;\n        ConfigManager.getInstance().setApi(api);\n        ActivityManagerService.onSystemServerContext(IApplicationThread.Stub.asInterface(appThread), activityToken);\n        registerBootCompleteReceiver();\n        registerPackageReceiver();\n        registerConfigurationReceiver();\n        registerSecretCodeReceiver();\n        registerUserChangeReceiver();\n        registerOpenManagerReceiver();\n        registerModuleScopeReceiver();\n        registerUidObserver();\n\n        if (ServiceManager.isLateInject) {\n            Log.i(TAG, \"System already booted during late injection. Manually triggering boot completed.\");\n            dispatchBootCompleted(null);\n        }\n    }\n\n    @Override\n    public boolean preStartManager() {\n        return ServiceManager.getManagerService().preStartManager();\n    }\n\n    @Override\n    public boolean setManagerEnabled(boolean enabled) throws RemoteException {\n        return ServiceManager.getManagerService().setEnabled(enabled);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/LogcatService.java",
    "content": "package org.lsposed.lspd.service;\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport android.os.ParcelFileDescriptor;\nimport android.os.Process;\nimport android.os.SELinux;\nimport android.os.SystemProperties;\nimport android.system.Os;\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.FileDescriptor;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.LinkedHashMap;\n\npublic class LogcatService implements Runnable {\n    private static final String TAG = \"LSPosedLogcat\";\n    private static final int mode = ParcelFileDescriptor.MODE_WRITE_ONLY |\n            ParcelFileDescriptor.MODE_CREATE |\n            ParcelFileDescriptor.MODE_TRUNCATE |\n            ParcelFileDescriptor.MODE_APPEND;\n    private int modulesFd = -1;\n    private int verboseFd = -1;\n    private Thread thread = null;\n\n    static class LogLRU extends LinkedHashMap<File, Object> {\n        private static final int MAX_ENTRIES = 10;\n\n        public LogLRU() {\n            super(MAX_ENTRIES, 1f, false);\n        }\n\n        @Override\n        synchronized protected boolean removeEldestEntry(Entry<File, Object> eldest) {\n            if (size() > MAX_ENTRIES && eldest.getKey().delete()) {\n                Log.d(TAG, \"Deleted old log \" + eldest.getKey().getAbsolutePath());\n                return true;\n            }\n            return false;\n        }\n    }\n\n    @SuppressWarnings(\"MismatchedQueryAndUpdateOfCollection\")\n    private final LinkedHashMap<File, Object> moduleLogs = new LogLRU();\n    @SuppressWarnings(\"MismatchedQueryAndUpdateOfCollection\")\n    private final LinkedHashMap<File, Object> verboseLogs = new LogLRU();\n\n    @SuppressLint(\"UnsafeDynamicallyLoadedCode\")\n    public LogcatService() {\n        String classPath = System.getProperty(\"java.class.path\");\n        var abi = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];\n        System.load(classPath + \"!/lib/\" + abi + \"/\" + System.mapLibraryName(\"daemon\"));\n        ConfigFileManager.moveLogDir();\n\n        // Meizu devices set this prop and prevent debug logs from being recorded\n        if (SystemProperties.getInt(\"persist.sys.log_reject_level\", 0) > 0) {\n            SystemProperties.set(\"persist.sys.log_reject_level\", \"0\");\n        }\n\n        getprop();\n        dmesg();\n    }\n\n    private static void getprop() {\n        // multithreaded process can not change their context type,\n        // start a new process to set restricted context to filter privacy props\n        var cmd = \"echo -n u:r:untrusted_app:s0 > /proc/thread-self/attr/current; getprop\";\n        try {\n            SELinux.setFSCreateContext(\"u:object_r:app_data_file:s0\");\n            new ProcessBuilder(\"sh\", \"-c\", cmd)\n                    .redirectOutput(ConfigFileManager.getPropsPath())\n                    .start();\n        } catch (IOException e) {\n            Log.e(TAG, \"getprop: \", e);\n        } finally {\n            SELinux.setFSCreateContext(null);\n        }\n    }\n\n    private static void dmesg() {\n        try {\n            new ProcessBuilder(\"dmesg\")\n                    .redirectOutput(ConfigFileManager.getKmsgPath())\n                    .start();\n        } catch (IOException e) {\n            Log.e(TAG, \"dmesg: \", e);\n        }\n    }\n\n    private native void runLogcat();\n\n    @Override\n    public void run() {\n        Log.i(TAG, \"start running\");\n        runLogcat();\n        Log.i(TAG, \"stopped\");\n    }\n\n    @SuppressWarnings(\"unused\")\n    private int refreshFd(boolean isVerboseLog) {\n        try {\n            File log;\n            if (isVerboseLog) {\n                checkFd(verboseFd);\n                log = ConfigFileManager.getNewVerboseLogPath();\n            } else {\n                checkFd(modulesFd);\n                log = ConfigFileManager.getNewModulesLogPath();\n            }\n            Log.i(TAG, \"New log file: \" + log);\n            ConfigFileManager.chattr0(log.toPath().getParent());\n            int fd = ParcelFileDescriptor.open(log, mode).detachFd();\n            if (isVerboseLog) {\n                synchronized (verboseLogs) {\n                    verboseLogs.put(log, new Object());\n                }\n                verboseFd = fd;\n            } else {\n                synchronized (moduleLogs) {\n                    moduleLogs.put(log, new Object());\n                }\n                modulesFd = fd;\n            }\n            return fd;\n        } catch (IOException e) {\n            if (isVerboseLog) verboseFd = -1;\n            else modulesFd = -1;\n            Log.w(TAG, \"refreshFd\", e);\n            return -1;\n        }\n    }\n\n    private static void checkFd(int fd) {\n        if (fd == -1) return;\n        try {\n            var jfd = new FileDescriptor();\n            //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi\n            jfd.getClass().getDeclaredMethod(\"setInt$\", int.class).invoke(jfd, fd);\n            var stat = Os.fstat(jfd);\n            if (stat.st_nlink == 0) {\n                var file = Files.readSymbolicLink(fdToPath(fd));\n                var parent = file.getParent();\n                if (!Files.isDirectory(parent, LinkOption.NOFOLLOW_LINKS)) {\n                    if (ConfigFileManager.chattr0(parent))\n                        Files.deleteIfExists(parent);\n                }\n                var name = file.getFileName().toString();\n                var originName = name.substring(0, name.lastIndexOf(' '));\n                Files.copy(file, parent.resolve(originName));\n            }\n        } catch (Throwable e) {\n            Log.w(TAG, \"checkFd \" + fd, e);\n        }\n    }\n\n    public boolean isRunning() {\n        return thread != null && thread.isAlive();\n    }\n\n    public void start() {\n        if (isRunning()) return;\n        thread = new Thread(this);\n        thread.setName(\"logcat\");\n        thread.setUncaughtExceptionHandler((t, e) -> {\n            Log.e(TAG, \"Crash unexpectedly: \", e);\n            thread = null;\n            start();\n        });\n        thread.start();\n    }\n\n    public void startVerbose() {\n        Log.i(TAG, \"!!start_verbose!!\");\n    }\n\n    public void stopVerbose() {\n        Log.i(TAG, \"!!stop_verbose!!\");\n    }\n\n    public void enableWatchdog() {\n        Log.i(TAG, \"!!start_watchdog!!\");\n    }\n\n    public void disableWatchdog() {\n        Log.i(TAG, \"!!stop_watchdog!!\");\n    }\n\n    public void refresh(boolean isVerboseLog) {\n        if (isVerboseLog) {\n            Log.i(TAG, \"!!refresh_verbose!!\");\n        } else {\n            Log.i(TAG, \"!!refresh_modules!!\");\n        }\n    }\n\n    private static Path fdToPath(int fd) {\n        if (fd == -1) return null;\n        else return Paths.get(\"/proc/self/fd\", String.valueOf(fd));\n    }\n\n    public File getVerboseLog() {\n        var path = fdToPath(verboseFd);\n        return path == null ? null : path.toFile();\n    }\n\n    public File getModulesLog() {\n        var path = fdToPath(modulesFd);\n        return path == null ? null : path.toFile();\n    }\n\n    public void checkLogFile() {\n        if (modulesFd == -1)\n            refresh(false);\n        if (verboseFd == -1)\n            refresh(true);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java",
    "content": "package org.lsposed.lspd.service;\n\nimport android.os.SharedMemory;\n\nimport java.util.HashMap;\n\npublic class ObfuscationManager {\n    // For module dexes\n    static native SharedMemory obfuscateDex(SharedMemory memory);\n\n    // generates signature\n    static native HashMap<String, String> getSignatures();\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/PackageService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS;\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\nimport static org.lsposed.lspd.service.ServiceManager.existsInGlobalNamespace;\n\nimport android.content.IIntentReceiver;\nimport android.content.IIntentSender;\nimport android.content.Intent;\nimport android.content.IntentSender;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ComponentInfo;\nimport android.content.pm.IPackageManager;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageInstaller;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ParceledListSlice;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.pm.VersionedPackage;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.os.ServiceManager;\nimport android.os.SystemProperties;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.lsposed.lspd.models.Application;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.stream.Collectors;\n\nimport rikka.parcelablelist.ParcelableListSlice;\n\npublic class PackageService {\n\n    static final int INSTALL_FAILED_INTERNAL_ERROR = -110;\n    static final int INSTALL_REASON_UNKNOWN = 0;\n    static final int MATCH_ANY_USER = 0x00400000; // PackageManager.MATCH_ANY_USER\n\n    static final int MATCH_ALL_FLAGS = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER;\n    public static final int PER_USER_RANGE = 100000;\n\n    private static IPackageManager pm = null;\n    private static IBinder binder = null;\n\n    static boolean isAlive() {\n        var pm = getPackageManager();\n        return pm != null && pm.asBinder().isBinderAlive();\n    }\n\n    private static final IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() {\n        @Override\n        public void binderDied() {\n            Log.w(TAG, \"pm is dead\");\n            binder.unlinkToDeath(this, 0);\n            binder = null;\n            pm = null;\n        }\n    };\n\n    private static IPackageManager getPackageManager() {\n        if (binder == null || pm == null) {\n            binder = ServiceManager.getService(\"package\");\n            if (binder == null) return null;\n            try {\n                binder.linkToDeath(recipient, 0);\n            } catch (RemoteException e) {\n                Log.e(TAG, Log.getStackTraceString(e));\n            }\n            pm = IPackageManager.Stub.asInterface(binder);\n        }\n        return pm;\n    }\n\n    @Nullable\n    public static PackageInfo getPackageInfo(String packageName, int flags, int userId) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        if (pm == null) return null;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            return pm.getPackageInfo(packageName, (long) flags, userId);\n        }\n        return pm.getPackageInfo(packageName, flags, userId);\n    }\n\n    public static @NonNull\n    Map<Integer, PackageInfo> getPackageInfoFromAllUsers(String packageName, int flags) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        Map<Integer, PackageInfo> res = new HashMap<>();\n        if (pm == null) return res;\n        for (var user : UserService.getUsers()) {\n            var info = getPackageInfo(packageName, flags, user.id);\n            if (info != null && info.applicationInfo != null) res.put(user.id, info);\n        }\n        return res;\n    }\n\n    @Nullable\n    public static ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        if (pm == null) return null;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            return pm.getApplicationInfo(packageName, (long) flags, userId);\n        }\n        return pm.getApplicationInfo(packageName, flags, userId);\n    }\n\n    // Only for manager\n    public static ParcelableListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags, boolean filterNoProcess) throws RemoteException {\n        List<PackageInfo> res = new ArrayList<>();\n        IPackageManager pm = getPackageManager();\n        if (pm == null) return ParcelableListSlice.emptyList();\n        for (var user : UserService.getUsers()) {\n            // in case pkginfo of other users in primary user\n            ParceledListSlice<PackageInfo> infos;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                infos = pm.getInstalledPackages((long) flags, user.id);\n            } else {\n                infos = pm.getInstalledPackages(flags, user.id);\n            }\n            res.addAll(infos\n                    .getList().parallelStream()\n                    .filter(info -> info.applicationInfo != null && info.applicationInfo.uid / PER_USER_RANGE == user.id)\n                    .filter(info -> {\n                        try {\n                            return isPackageAvailable(info.packageName, user.id, true);\n                        } catch (RemoteException e) {\n                            return false;\n                        }\n                    })\n                    .collect(Collectors.toList()));\n        }\n        if (filterNoProcess) {\n            return new ParcelableListSlice<>(res.parallelStream().filter(packageInfo -> {\n                try {\n                    PackageInfo pkgInfo = getPackageInfoWithComponents(packageInfo.packageName, MATCH_ALL_FLAGS, packageInfo.applicationInfo.uid / PER_USER_RANGE);\n                    return !fetchProcesses(pkgInfo).isEmpty();\n                } catch (RemoteException e) {\n                    Log.w(TAG, \"filter failed\", e);\n                    return true;\n                }\n            }).collect(Collectors.toList()));\n        }\n        return new ParcelableListSlice<>(res);\n    }\n\n    private static Set<String> fetchProcesses(PackageInfo pkgInfo) {\n        HashSet<String> processNames = new HashSet<>();\n        if (pkgInfo == null) return processNames;\n        for (ComponentInfo[] componentInfos : new ComponentInfo[][]{pkgInfo.activities, pkgInfo.receivers, pkgInfo.providers}) {\n            if (componentInfos == null) continue;\n            for (ComponentInfo componentInfo : componentInfos) {\n                processNames.add(componentInfo.processName);\n            }\n        }\n        if (pkgInfo.services == null) return processNames;\n        for (ServiceInfo service : pkgInfo.services) {\n            if ((service.flags & FLAG_ISOLATED_PROCESS) == 0) {\n                processNames.add(service.processName);\n            }\n        }\n        return processNames;\n    }\n\n    public static Pair<Set<String>, Integer> fetchProcessesWithUid(Application app) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        if (pm == null) return new Pair<>(Collections.emptySet(), -1);\n        PackageInfo pkgInfo = getPackageInfoWithComponents(app.packageName, MATCH_ALL_FLAGS, app.userId);\n        if (pkgInfo == null || pkgInfo.applicationInfo == null)\n            return new Pair<>(Collections.emptySet(), -1);\n        return new Pair<>(fetchProcesses(pkgInfo), pkgInfo.applicationInfo.uid);\n    }\n\n    public static boolean isPackageAvailable(String packageName, int userId, boolean ignoreHidden) throws RemoteException {\n        return pm.isPackageAvailable(packageName, userId) || (ignoreHidden && pm.getApplicationHiddenSettingAsUser(packageName, userId));\n    }\n\n    @SuppressWarnings({\"ConstantConditions\", \"SameParameterValue\"})\n    @Nullable\n    private static PackageInfo getPackageInfoWithComponents(String packageName, int flags, int userId) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        if (pm == null) return null;\n        PackageInfo pkgInfo;\n        try {\n            pkgInfo = getPackageInfo(packageName, flags | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS, userId);\n        } catch (Exception e) {\n            pkgInfo = getPackageInfo(packageName, flags, userId);\n            if (pkgInfo == null) return null;\n            try {\n                pkgInfo.activities = getPackageInfo(packageName, flags | PackageManager.GET_ACTIVITIES, userId).activities;\n            } catch (Exception ignored) {\n\n            }\n            try {\n                pkgInfo.services = getPackageInfo(packageName, flags | PackageManager.GET_SERVICES, userId).services;\n            } catch (Exception ignored) {\n\n            }\n            try {\n                pkgInfo.receivers = getPackageInfo(packageName, flags | PackageManager.GET_RECEIVERS, userId).receivers;\n            } catch (Exception ignored) {\n\n            }\n            try {\n                pkgInfo.providers = getPackageInfo(packageName, flags | PackageManager.GET_PROVIDERS, userId).providers;\n            } catch (Exception ignored) {\n\n            }\n        }\n        if (pkgInfo == null || pkgInfo.applicationInfo == null || (!pkgInfo.packageName.equals(\"android\") && (pkgInfo.applicationInfo.sourceDir == null || !existsInGlobalNamespace(pkgInfo.applicationInfo.sourceDir) || !isPackageAvailable(packageName, userId, true))))\n            return null;\n        return pkgInfo;\n    }\n\n    static abstract class IntentSenderAdaptor extends IIntentSender.Stub {\n        public abstract void send(Intent intent);\n\n        @Override\n        public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {\n            send(intent);\n            return 0;\n        }\n\n        @Override\n        public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {\n            send(intent);\n        }\n\n        public IntentSender getIntentSender() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {\n            @SuppressWarnings(\"JavaReflectionMemberAccess\")\n            Constructor<IntentSender> intentSenderConstructor = IntentSender.class.getConstructor(IIntentSender.class);\n            intentSenderConstructor.setAccessible(true);\n            return intentSenderConstructor.newInstance(this);\n        }\n    }\n\n    public static boolean uninstallPackage(VersionedPackage versionedPackage, int userId) throws RemoteException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {\n        CountDownLatch latch = new CountDownLatch(1);\n        final boolean[] result = {false};\n        var flag = userId == -1 ? 0x00000002 : 0; //PackageManager.DELETE_ALL_USERS = 0x00000002; UserHandle ALL = new UserHandle(-1);\n        pm.getPackageInstaller().uninstall(versionedPackage, \"android\", flag, new IntentSenderAdaptor() {\n            @Override\n            public void send(Intent intent) {\n                int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);\n                result[0] = status == PackageInstaller.STATUS_SUCCESS;\n                Log.d(TAG, intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));\n                latch.countDown();\n            }\n        }.getIntentSender(), userId == -1 ? 0 : userId);\n        latch.await();\n        return result[0];\n    }\n\n    public static int installExistingPackageAsUser(String packageName, int userId) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        Log.d(TAG, \"about to install existing package \" + packageName + \"/\" + userId);\n        if (pm == null) return INSTALL_FAILED_INTERNAL_ERROR;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            return pm.installExistingPackageAsUser(packageName, userId, 0, INSTALL_REASON_UNKNOWN, null);\n        } else {\n            return pm.installExistingPackageAsUser(packageName, userId, 0, INSTALL_REASON_UNKNOWN);\n        }\n    }\n\n    @Nullable\n    public static ParcelableListSlice<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            IPackageManager pm = getPackageManager();\n            if (pm == null) return null;\n            ParceledListSlice<ResolveInfo> infos;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                infos = pm.queryIntentActivities(intent, resolvedType, (long) flags, userId);\n            } else {\n                infos = pm.queryIntentActivities(intent, resolvedType, flags, userId);\n            }\n            return new ParcelableListSlice<>(infos.getList());\n        } catch (Exception e) {\n            Log.e(TAG, \"queryIntentActivities\", e);\n            return new ParcelableListSlice<>(new ArrayList<>());\n        }\n    }\n\n    @Nullable\n    public static Intent getLaunchIntentForPackage(String packageName) throws RemoteException {\n        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);\n        intentToResolve.addCategory(Intent.CATEGORY_INFO);\n        intentToResolve.setPackage(packageName);\n        var ris = queryIntentActivities(intentToResolve, intentToResolve.getType(), 0, 0);\n\n        // Otherwise, try to find a main launcher activity.\n        if (ris == null || ris.getList().size() == 0) {\n            // reuse the intent instance\n            intentToResolve.removeCategory(Intent.CATEGORY_INFO);\n            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);\n            intentToResolve.setPackage(packageName);\n            ris = queryIntentActivities(intentToResolve, intentToResolve.getType(), 0, 0);\n        }\n        if (ris == null || ris.getList().size() == 0) {\n            return null;\n        }\n        Intent intent = new Intent(intentToResolve);\n        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        intent.setClassName(ris.getList().get(0).activityInfo.packageName,\n                ris.getList().get(0).activityInfo.name);\n        return intent;\n    }\n\n    public static void clearApplicationProfileData(String packageName) throws RemoteException {\n        IPackageManager pm = getPackageManager();\n        if (pm == null) return;\n        pm.clearApplicationProfileData(packageName);\n    }\n\n    public static boolean performDexOptMode(String packageName) throws RemoteException {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {\n            Process process = null;\n            try {\n                // The 'speed-profile' filter is a balanced choice for performance.\n                String command = \"cmd package compile -m speed-profile -f \" + packageName;\n                process = Runtime.getRuntime().exec(command);\n\n                // Capture and log the output for debugging.\n                StringBuilder output = new StringBuilder();\n                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {\n                    String line;\n                    while ((line = reader.readLine()) != null) {\n                        output.append(line).append(\"\\n\");\n                    }\n                }\n\n                int exitCode = process.waitFor();\n                Log.i(TAG, \"Dexopt command finished for \" + packageName + \" with exit code: \" + exitCode);\n\n                // A successful command returns exit code 0 and typically \"Success\" in its output.\n                return exitCode == 0 && output.toString().contains(\"Success\");\n\n            } catch (Exception e) {\n                Log.e(TAG, \"Failed to execute dexopt shell command for \" + packageName, e);\n                if (e instanceof InterruptedException) {\n                    // Preserve the interrupted status.\n                    Thread.currentThread().interrupt();\n                }\n                return false;\n            } finally {\n                if (process != null) {\n                    process.destroy();\n                }\n            }\n        } else {\n            // Fallback to the original reflection method for older Android versions.\n            IPackageManager pm = getPackageManager();\n            if (pm == null) return false;\n            return pm.performDexOptMode(packageName,\n                SystemProperties.getBoolean(\"dalvik.vm.usejitprofiles\", false),\n                SystemProperties.get(\"pm.dexopt.install\", \"speed-profile\"), true, true, null);\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/PowerService.java",
    "content": "/*\n * <!--This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2023 LSPosed Contributors-->\n */\n\npackage org.lsposed.lspd.service;\n\nimport static android.content.Context.POWER_SERVICE;\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.os.IBinder;\nimport android.os.IPowerManager;\nimport android.os.RemoteException;\nimport android.os.ServiceManager;\nimport android.util.Log;\n\npublic class PowerService {\n    private static IPowerManager pm = null;\n    private static IBinder binder = null;\n    private static final IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() {\n        @Override\n        public void binderDied() {\n            Log.w(TAG, \"PowerManager is dead\");\n            binder.unlinkToDeath(this, 0);\n            binder = null;\n            pm = null;\n        }\n    };\n\n    private static IPowerManager getPowerManager() {\n        if (binder == null || pm == null) {\n            binder = ServiceManager.getService(POWER_SERVICE);\n            if (binder == null) return null;\n            try {\n                binder.linkToDeath(recipient, 0);\n            } catch (RemoteException e) {\n                Log.e(TAG, Log.getStackTraceString(e));\n            }\n            pm = IPowerManager.Stub.asInterface(binder);\n        }\n        return pm;\n    }\n\n    public static void reboot(boolean confirm, String reason, boolean wait) throws RemoteException {\n        IPowerManager pm = getPowerManager();\n        if (pm == null) return;\n        pm.reboot(confirm, reason, wait);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport android.app.ActivityThread;\nimport android.app.Notification;\nimport android.content.Context;\nimport android.ddm.DdmHandleAppName;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IServiceManager;\nimport android.os.Looper;\nimport android.os.Parcel;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\n\nimport com.android.internal.os.BinderInternal;\n\nimport org.lsposed.daemon.BuildConfig;\nimport org.lsposed.lspd.util.FakeContext;\n\nimport java.io.File;\nimport java.lang.AbstractMethodError;\nimport java.lang.Class;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport hidden.HiddenApiBridge;\n\npublic class ServiceManager {\n    public static final String TAG = \"LSPosedService\";\n    private static final File globalNamespace = new File(\"/proc/1/root\");\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private static LSPosedService mainService = null;\n    private static LSPApplicationService applicationService = null;\n    private static LSPManagerService managerService = null;\n    private static LSPSystemServerService systemServerService = null;\n    private static LogcatService logcatService = null;\n    private static Dex2OatService dex2OatService = null;\n\n    public static boolean isLateInject = false;\n    public static String proxyServiceName = \"serial\";\n\n    private static final ExecutorService executorService = Executors.newSingleThreadExecutor();\n\n    @RequiresApi(Build.VERSION_CODES.Q)\n    public static Dex2OatService getDex2OatService() {\n        return dex2OatService;\n    }\n\n    public static ExecutorService getExecutorService() {\n        return executorService;\n    }\n\n    private static void waitSystemService(String name) {\n        while (android.os.ServiceManager.getService(name) == null) {\n            try {\n                Log.i(TAG, \"service \" + name + \" is not started, wait 1s.\");\n                //noinspection BusyWait\n                Thread.sleep(1000);\n            } catch (InterruptedException e) {\n                Log.i(TAG, Log.getStackTraceString(e));\n            }\n        }\n    }\n\n    public static IServiceManager getSystemServiceManager() {\n        return IServiceManager.Stub.asInterface(HiddenApiBridge.Binder_allowBlocking(BinderInternal.getContextObject()));\n    }\n\n    // call by ourselves\n    public static void start(String[] args) {\n        if (!ConfigFileManager.tryLock()) System.exit(0);\n\n        int systemServerMaxRetry = 1;\n        for (String arg : args) {\n            if (arg.startsWith(\"--system-server-max-retry=\")) {\n                try {\n                    systemServerMaxRetry = Integer.parseInt(arg.substring(arg.lastIndexOf('=') + 1));\n                } catch (Throwable ignored) {\n                }\n            } else if (arg.equals(\"--late-inject\")) {\n                isLateInject = true;\n                proxyServiceName = \"serial_vector\";\n            }\n        }\n\n        Log.i(TAG, \"Vector daemon started: lateInject: \" + isLateInject);\n        Log.i(TAG, String.format(\"version %s (%d)\", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));\n\n        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {\n            Log.e(TAG, \"Uncaught exception\", e);\n            System.exit(1);\n        });\n\n        logcatService = new LogcatService();\n        logcatService.start();\n\n        // get config before package service is started\n        // otherwise getInstance will trigger module/scope cache\n        var configManager = ConfigManager.getInstance();\n        // --- DO NOT call ConfigManager.getInstance later!!! ---\n\n        // Unblock log watchdog before starting anything else\n        if (configManager.isLogWatchdogEnabled())\n            logcatService.enableWatchdog();\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)\n            permissionManagerWorkaround();\n\n        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);\n        Looper.prepareMainLooper();\n\n\n        mainService = new LSPosedService();\n        applicationService = new LSPApplicationService();\n        managerService = new LSPManagerService();\n        systemServerService = new LSPSystemServerService(systemServerMaxRetry, proxyServiceName);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            dex2OatService = new Dex2OatService();\n            dex2OatService.start();\n        }\n\n        systemServerService.putBinderForSystemServer();\n\n        ActivityThread.systemMain();\n\n        DdmHandleAppName.setAppName(\"org.lsposed.daemon\", 0);\n\n        waitSystemService(\"package\");\n        waitSystemService(\"activity\");\n        waitSystemService(Context.USER_SERVICE);\n        waitSystemService(Context.APP_OPS_SERVICE);\n\n        ConfigFileManager.reloadConfiguration();\n\n        notificationWorkaround();\n\n        BridgeService.send(mainService, new BridgeService.Listener() {\n            @Override\n            public void onSystemServerRestarted() {\n                Log.w(TAG, \"system restarted...\");\n            }\n\n            @Override\n            public void onResponseFromBridgeService(boolean response) {\n                if (response) {\n                    Log.i(TAG, \"sent service to bridge\");\n                } else {\n                    Log.w(TAG, \"no response from bridge\");\n                }\n                systemServerService.maybeRetryInject();\n            }\n\n            @Override\n            public void onSystemServerDied() {\n                Log.w(TAG, \"system server died\");\n                systemServerService.putBinderForSystemServer();\n                managerService.onSystemServerDied();\n            }\n        });\n\n        // Force logging on boot, now let's see if we need to stop logging\n        if (!configManager.verboseLog()) {\n            logcatService.stopVerbose();\n        }\n\n        Looper.loop();\n        throw new RuntimeException(\"Main thread loop unexpectedly exited\");\n    }\n\n    public static LSPApplicationService getApplicationService() {\n        return applicationService;\n    }\n\n    public static LSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) {\n        if (applicationService.registerHeartBeat(uid, pid, processName, heartBeat))\n            return applicationService;\n        else return null;\n    }\n\n    public static LSPManagerService getManagerService() {\n        return managerService;\n    }\n\n    public static LogcatService getLogcatService() {\n        return logcatService;\n    }\n\n    public static boolean systemServerRequested() {\n        return systemServerService.systemServerRequested();\n    }\n\n    public static File toGlobalNamespace(File file) {\n        return new File(globalNamespace, file.getAbsolutePath());\n    }\n\n    public static File toGlobalNamespace(String path) {\n        if (path == null) return null;\n        if (path.startsWith(\"/\")) return new File(globalNamespace, path);\n        else return toGlobalNamespace(new File(path));\n    }\n\n    public static boolean existsInGlobalNamespace(File file) {\n        return toGlobalNamespace(file).exists();\n    }\n\n    public static boolean existsInGlobalNamespace(String path) {\n        return toGlobalNamespace(path).exists();\n    }\n\n    private static void permissionManagerWorkaround() {\n        try {\n            Field sCacheField = android.os.ServiceManager.class.getDeclaredField(\"sCache\");\n            sCacheField.setAccessible(true);\n            var sCache = (Map) sCacheField.get(null);\n            sCache.put(\"permissionmgr\", new BinderProxy(\"permissionmgr\"));\n            sCache.put(\"legacy_permission\", new BinderProxy(\"legacy_permission\"));\n            sCache.put(\"appops\", new BinderProxy(\"appops\"));\n        } catch (Throwable e) {\n            Log.e(TAG, \"failed to init permission manager\", e);\n        }\n    }\n\n    private static void notificationWorkaround() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {\n            try {\n                Class feature = Class.forName(\"android.app.FeatureFlagsImpl\");\n                Field systemui_is_cached = feature.getDeclaredField(\"systemui_is_cached\");\n                systemui_is_cached.setAccessible(true);\n                systemui_is_cached.set(null, true);\n                Log.d(TAG, \"set flag systemui_is_cached to true\");\n            } catch (Throwable e) {\n                Log.e(TAG, \"failed to change feature flags\", e);\n            }\n        }\n\n        try {\n            new Notification.Builder(new FakeContext(), \"notification_workaround\").build();\n        } catch (AbstractMethodError e) {\n            FakeContext.nullProvider = ! FakeContext.nullProvider;\n        } catch (Throwable e) {\n            Log.e(TAG, \"failed to build notifications\", e);\n        }\n\n    }\n\n    private static class BinderProxy extends Binder {\n        private static final Method rawGetService;\n\n        static {\n            try {\n                rawGetService = android.os.ServiceManager.class.getDeclaredMethod(\"rawGetService\", String.class);\n                rawGetService.setAccessible(true);\n            } catch (NoSuchMethodException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        private IBinder mReal = null;\n        private final String mName;\n\n        BinderProxy(String name) {\n            mName = name;\n        }\n\n        @Override\n        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {\n            synchronized (this) {\n                if (mReal == null) {\n                    try {\n                        mReal = (IBinder) rawGetService.invoke(null, mName);\n                    } catch (IllegalAccessException | InvocationTargetException ignored){\n\n                    }\n                }\n                if (mReal != null) {\n                    return mReal.transact(code, data, reply, flags);\n                }\n            }\n            // getSplitPermissions\n            if (reply != null && mName.equals(\"permissionmgr\"))\n                reply.writeTypedList(List.of());\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/service/UserService.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.service;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.content.Context;\nimport android.content.pm.UserInfo;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IUserManager;\nimport android.os.RemoteException;\nimport android.os.ServiceManager;\nimport android.util.Log;\n\nimport org.lsposed.lspd.util.Utils;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\npublic class UserService {\n    private static IUserManager um = null;\n    private static IBinder binder = null;\n    private static final IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() {\n        @Override\n        public void binderDied() {\n            Log.w(TAG, \"um is dead\");\n            binder.unlinkToDeath(this, 0);\n            binder = null;\n            um = null;\n        }\n    };\n\n    static boolean isAlive() {\n        var um = getUserManager();\n        return um != null && um.asBinder().isBinderAlive();\n    }\n\n    public static IUserManager getUserManager() {\n        if (binder == null || um == null) {\n            binder = ServiceManager.getService(Context.USER_SERVICE);\n            if (binder == null) return null;\n            try {\n                binder.linkToDeath(recipient, 0);\n            } catch (RemoteException e) {\n                Log.e(TAG, Log.getStackTraceString(e));\n            }\n            um = IUserManager.Stub.asInterface(binder);\n        }\n        return um;\n    }\n\n    public static List<UserInfo> getUsers() throws RemoteException {\n        IUserManager um = getUserManager();\n        List<UserInfo> users = new LinkedList<>();\n        if (um == null) return users;\n        try {\n            users = um.getUsers(true);\n        } catch (NoSuchMethodError e) {\n            users = um.getUsers(true, true, true);\n        }\n        if (Utils.isLENOVO) { // lenovo hides user [900, 910) for app cloning\n            var gotUsers = new boolean[10];\n            for (var user : users) {\n                var residual = user.id - 900;\n                if (residual >= 0 && residual < 10) gotUsers[residual] = true;\n            }\n            for (int i = 900; i <= 909; i++) {\n                var user = um.getUserInfo(i);\n                if (user != null && !gotUsers[i - 900]) {\n                    users.add(user);\n                }\n            }\n        }\n        return users;\n    }\n\n    public static UserInfo getUserInfo(int userId) throws RemoteException {\n        IUserManager um = getUserManager();\n        if (um == null) return null;\n        return um.getUserInfo(userId);\n    }\n\n    public static String getUserName(int userId) {\n        try {\n            var userInfo = getUserInfo(userId);\n            if (userInfo != null) return userInfo.name;\n        } catch (RemoteException ignored) {\n        }\n        return String.valueOf(userId);\n    }\n\n    public static int getProfileParent(int userId) throws RemoteException {\n        IUserManager um = getUserManager();\n        if (um == null) return -1;\n        var userInfo = um.getProfileParent(userId);\n        if (userInfo == null) return userId;\n        else return userInfo.id;\n    }\n\n    public static boolean isUserUnlocked(int userId) throws RemoteException {\n        IUserManager um = getUserManager();\n        if (um == null) return false;\n        return um.isUserUnlocked(userId);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/util/FakeContext.java",
    "content": "package org.lsposed.lspd.util;\n\nimport static org.lsposed.lspd.service.ServiceManager.TAG;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.Resources;\nimport android.os.UserHandle;\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\n\nimport org.lsposed.lspd.service.ConfigFileManager;\nimport org.lsposed.lspd.service.PackageService;\n\nimport hidden.HiddenApiBridge;\n\npublic class FakeContext extends ContextWrapper {\n    static ApplicationInfo systemApplicationInfo = null;\n    static Resources.Theme theme = null;\n\n    public static Boolean nullProvider = false;\n\n    private String packageName = \"android\";\n    public FakeContext() {\n        super(null);\n    }\n\n    public FakeContext(String packageName) {\n        super(null);\n        this.packageName = packageName;\n    }\n\n    @Override\n    public String getPackageName() {\n        return packageName;\n    }\n\n    @Override\n    public Resources getResources() {\n        return ConfigFileManager.getResources();\n    }\n\n    @Override\n    public String getOpPackageName() {\n        return \"android\";\n    }\n\n    @Override\n    public ApplicationInfo getApplicationInfo() {\n        try {\n            if (systemApplicationInfo == null)\n                systemApplicationInfo = PackageService.getApplicationInfo(\"android\", 0, 0);\n        } catch (Throwable e) {\n            Log.e(TAG, \"getApplicationInfo\", e);\n        }\n        return systemApplicationInfo;\n    }\n\n    @Override\n    public ContentResolver getContentResolver() {\n        if (nullProvider) {\n            return null;\n        } else {\n            return new ContentResolver(this) {};\n        }\n    }\n\n    public int getUserId() {\n        return 0;\n    }\n\n    public UserHandle getUser() {\n        return HiddenApiBridge.UserHandle(0);\n    }\n\n    @Override\n    public Resources.Theme getTheme() {\n        if (theme == null) theme = getResources().newTheme();\n        return theme;\n    }\n\n    @Nullable\n    @Override\n    public String getAttributionTag() {\n        return null;\n    }\n\n    @Override\n    public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {\n        throw new PackageManager.NameNotFoundException(packageName);\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/java/org/lsposed/lspd/util/InstallerVerifier.java",
    "content": "package org.lsposed.lspd.util;\n\nimport static org.lsposed.lspd.util.SignInfo.CERTIFICATE;\n\nimport com.android.apksig.ApkVerifier;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\n\npublic class InstallerVerifier {\n\n    public static void verifyInstallerSignature(String path) throws IOException {\n        ApkVerifier verifier = new ApkVerifier.Builder(new File(path))\n                .setMinCheckedPlatformVersion(27)\n                .build();\n        try {\n            ApkVerifier.Result result = verifier.verify();\n            if (!result.isVerified()) {\n                throw new IOException(\"apk signature not verified\");\n            }\n            var mainCert = result.getSignerCertificates().get(0);\n            if (!Arrays.equals(mainCert.getEncoded(), CERTIFICATE)) {\n                var dname = mainCert.getSubjectX500Principal().getName();\n                throw new IOException(\"apk signature mismatch: \" + dname);\n            }\n        } catch (Exception t) {\n            throw new IOException(t);\n        }\n    }\n}\n"
  },
  {
    "path": "daemon/src/main/jni/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(daemon)\n\nset(CMAKE_CXX_STANDARD 23)\n\nadd_subdirectory(${VECTOR_ROOT}/external external)\n\nset(SOURCES\n        dex2oat.cpp\n        logcat.cpp\n        obfuscation.cpp\n        )\n\nadd_library(${PROJECT_NAME} SHARED ${SOURCES})\n\ntarget_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})\n\ntarget_link_libraries(${PROJECT_NAME} PRIVATE lsplant_static dex_builder_static android log)\n\nif (DEFINED DEBUG_SYMBOLS_PATH)\n    message(STATUS \"Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}\")\n    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD\n            COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}\n            COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:${PROJECT_NAME}>\n            ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug\n            COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>\n            COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug\n            $<TARGET_FILE:${PROJECT_NAME}>)\nendif()\n"
  },
  {
    "path": "daemon/src/main/jni/dex2oat.cpp",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2023 LSPosed Contributors\n */\n#include <fcntl.h>\n#include <jni.h>\n#include <sched.h>\n#include <stdlib.h>\n#include <sys/mount.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <string>\n\n#include \"logging.h\"\n\nextern \"C\" JNIEXPORT void JNICALL Java_org_lsposed_lspd_service_Dex2OatService_doMountNative(\n    JNIEnv *env, jobject, jboolean enabled, jstring r32, jstring d32, jstring r64, jstring d64) {\n    char dex2oat32[PATH_MAX], dex2oat64[PATH_MAX];\n    realpath(\"bin/dex2oat32\", dex2oat32);\n    realpath(\"bin/dex2oat64\", dex2oat64);\n\n    if (pid_t pid = fork(); pid > 0) {  // parent\n        waitpid(pid, nullptr, 0);\n    } else {  // child\n        int ns = open(\"/proc/1/ns/mnt\", O_RDONLY);\n        setns(ns, CLONE_NEWNS);\n        close(ns);\n\n        const char *r32p, *d32p, *r64p, *d64p;\n        if (r32) r32p = env->GetStringUTFChars(r32, nullptr);\n        if (d32) d32p = env->GetStringUTFChars(d32, nullptr);\n        if (r64) r64p = env->GetStringUTFChars(r64, nullptr);\n        if (d64) d64p = env->GetStringUTFChars(d64, nullptr);\n\n        if (enabled) {\n            LOGI(\"Enable dex2oat wrapper\");\n            if (r32) {\n                mount(dex2oat32, r32p, nullptr, MS_BIND, nullptr);\n                mount(nullptr, r32p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr);\n            }\n            if (d32) {\n                mount(dex2oat32, d32p, nullptr, MS_BIND, nullptr);\n                mount(nullptr, d32p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr);\n            }\n            if (r64) {\n                mount(dex2oat64, r64p, nullptr, MS_BIND, nullptr);\n                mount(nullptr, r64p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr);\n            }\n            if (d64) {\n                mount(dex2oat64, d64p, nullptr, MS_BIND, nullptr);\n                mount(nullptr, d64p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr);\n            }\n            execlp(\"resetprop\", \"resetprop\", \"--delete\", \"dalvik.vm.dex2oat-flags\", nullptr);\n        } else {\n            LOGI(\"Disable dex2oat wrapper\");\n            if (r32) umount(r32p);\n            if (d32) umount(d32p);\n            if (r64) umount(r64p);\n            if (d64) umount(d64p);\n            execlp(\"resetprop\", \"resetprop\", \"dalvik.vm.dex2oat-flags\", \"--inline-max-code-units=0\",\n                   nullptr);\n        }\n\n        PLOGE(\"Failed to resetprop\");\n        exit(1);\n    }\n}\n\nstatic int setsockcreatecon_raw(const char *context) {\n    std::string path = \"/proc/self/task/\" + std::to_string(gettid()) + \"/attr/sockcreate\";\n    int fd = open(path.c_str(), O_RDWR | O_CLOEXEC);\n    if (fd < 0) return -1;\n    int ret;\n    if (context) {\n        do {\n            ret = write(fd, context, strlen(context) + 1);\n        } while (ret < 0 && errno == EINTR);\n    } else {\n        do {\n            ret = write(fd, nullptr, 0);  // clear\n        } while (ret < 0 && errno == EINTR);\n    }\n    close(fd);\n    return ret < 0 ? -1 : 0;\n}\n\nextern \"C\" JNIEXPORT jboolean JNICALL\nJava_org_lsposed_lspd_service_Dex2OatService_setSockCreateContext(JNIEnv *env, jclass,\n                                                                  jstring contextStr) {\n    const char *context = env->GetStringUTFChars(contextStr, nullptr);\n    int ret = setsockcreatecon_raw(context);\n    env->ReleaseStringUTFChars(contextStr, context);\n    return ret == 0;\n}\n\nextern \"C\" JNIEXPORT jstring JNICALL\nJava_org_lsposed_lspd_service_Dex2OatService_getSockPath(JNIEnv *env, jobject) {\n    return env->NewStringUTF(\"5291374ceda0aef7c5d86cd2a4f6a3ac\\0\");\n}\n"
  },
  {
    "path": "daemon/src/main/jni/logcat.cpp",
    "content": "#include \"logcat.h\"\n\n#include <android/log.h>\n#include <jni.h>\n#include <sys/system_properties.h>\n#include <unistd.h>\n\n#include <array>\n#include <atomic>\n#include <functional>\n#include <string>\n#include <thread>\n\nusing namespace std::string_view_literals;\nusing namespace std::chrono_literals;\n\nconstexpr size_t kMaxLogSize = 4 * 1024 * 1024;\nconstexpr long kLogBufferSize = 128 * 1024;\n\nnamespace {\nconstexpr std::array<char, ANDROID_LOG_SILENT + 1> kLogChar = {\n    /*ANDROID_LOG_UNKNOWN*/ '?',\n    /*ANDROID_LOG_DEFAULT*/ '?',\n    /*ANDROID_LOG_VERBOSE*/ 'V',\n    /*ANDROID_LOG_DEBUG*/ 'D',\n    /*ANDROID_LOG_INFO*/ 'I',\n    /*ANDROID_LOG_WARN*/ 'W',\n    /*ANDROID_LOG_ERROR*/ 'E',\n    /*ANDROID_LOG_FATAL*/ 'F',\n    /*ANDROID_LOG_SILENT*/ 'S',\n};\n\nlong ParseUint(const char *s) {\n    if (s[0] == '\\0') return -1;\n\n    while (isspace(*s)) {\n        s++;\n    }\n\n    if (s[0] == '-') {\n        return -1;\n    }\n\n    int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;\n    char *end;\n    auto result = strtoull(s, &end, base);\n    if (end == s) {\n        return -1;\n    }\n    if (*end != '\\0') {\n        const char *suffixes = \"bkmgtpe\";\n        const char *suffix;\n        if ((suffix = strchr(suffixes, tolower(*end))) == nullptr ||\n            __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {\n            return -1;\n        }\n    }\n    if (std::numeric_limits<size_t>::max() < result) {\n        return -1;\n    }\n    return static_cast<size_t>(result);\n}\n\ninline long GetByteProp(std::string_view prop, long def = -1) {\n    std::array<char, PROP_VALUE_MAX> buf{};\n    if (__system_property_get(prop.data(), buf.data()) < 0) return def;\n    return ParseUint(buf.data());\n}\n\ninline std::string GetStrProp(std::string_view prop, std::string def = {}) {\n    std::array<char, PROP_VALUE_MAX> buf{};\n    if (__system_property_get(prop.data(), buf.data()) < 0) return def;\n    return {buf.data()};\n}\n\ninline bool SetIntProp(std::string_view prop, int val) {\n    auto buf = std::to_string(val);\n    return __system_property_set(prop.data(), buf.data()) >= 0;\n}\n\ninline bool SetStrProp(std::string_view prop, std::string_view val) {\n    return __system_property_set(prop.data(), val.data()) >= 0;\n}\n\n}  // namespace\n\nclass UniqueFile : public std::unique_ptr<FILE, std::function<void(FILE *)>> {\n    inline static deleter_type deleter = [](auto f) { f &&f != stdout &&fclose(f); };\n\npublic:\n    explicit UniqueFile(FILE *f) : std::unique_ptr<FILE, std::function<void(FILE *)>>(f, deleter) {}\n\n    UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {};\n\n    UniqueFile() : UniqueFile(stdout) {};\n};\n\nclass Logcat {\npublic:\n    explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method)\n        : env_(env), thiz_(thiz), refresh_fd_method_(method) {}\n\n    [[noreturn]] void Run();\n\nprivate:\n    inline void RefreshFd(bool is_verbose);\n\n    inline void Log(std::string_view str);\n\n    void OnCrash(int err);\n\n    void ProcessBuffer(struct log_msg *buf);\n\n    static size_t PrintLogLine(const AndroidLogEntry &entry, FILE *out);\n\n    void StartLogWatchDog();\n\n    JNIEnv *env_;\n    jobject thiz_;\n    jmethodID refresh_fd_method_;\n\n    UniqueFile modules_file_{};\n    size_t modules_file_part_ = 0;\n    size_t modules_print_count_ = 0;\n\n    UniqueFile verbose_file_{};\n    size_t verbose_file_part_ = 0;\n    size_t verbose_print_count_ = 0;\n\n    pid_t my_pid_ = getpid();\n\n    bool verbose_ = true;\n    std::atomic<bool> enable_watchdog = std::atomic<bool>(false);\n};\n\nsize_t Logcat::PrintLogLine(const AndroidLogEntry &entry, FILE *out) {\n    if (!out) return 0;\n    constexpr static size_t kMaxTimeBuff = 64;\n    struct tm tm{};\n    std::array<char, kMaxTimeBuff> time_buff{};\n\n    auto now = entry.tv_sec;\n    auto nsec = entry.tv_nsec;\n    auto message_len = entry.messageLen;\n    const auto *message = entry.message;\n    if (now < 0) {\n        nsec = NS_PER_SEC - nsec;\n    }\n    if (message_len >= 1 && message[message_len - 1] == '\\n') {\n        --message_len;\n    }\n    localtime_r(&now, &tm);\n    strftime(time_buff.data(), time_buff.size(), \"%Y-%m-%dT%H:%M:%S\", &tm);\n    int len =\n        fprintf(out, \"[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\\n\", time_buff.data(),\n                nsec / MS_PER_NSEC, entry.uid, entry.pid, entry.tid, kLogChar[entry.priority],\n                static_cast<int>(entry.tagLen), entry.tag, static_cast<int>(message_len), message);\n    fflush(out);\n    // trigger overflow when failed to generate a new fd\n    if (len <= 0) len = kMaxLogSize;\n    return static_cast<size_t>(len);\n}\n\nvoid Logcat::RefreshFd(bool is_verbose) {\n    constexpr auto start = \"----part %zu start----\\n\";\n    constexpr auto end = \"-----part %zu end----\\n\";\n    if (is_verbose) {\n        verbose_print_count_ = 0;\n        fprintf(verbose_file_.get(), end, verbose_file_part_);\n        fflush(verbose_file_.get());\n        verbose_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_TRUE), \"a\");\n        verbose_file_part_++;\n        fprintf(verbose_file_.get(), start, verbose_file_part_);\n        fflush(verbose_file_.get());\n    } else {\n        modules_print_count_ = 0;\n        fprintf(modules_file_.get(), end, modules_file_part_);\n        fflush(modules_file_.get());\n        modules_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_FALSE), \"a\");\n        modules_file_part_++;\n        fprintf(modules_file_.get(), start, modules_file_part_);\n        fflush(modules_file_.get());\n    }\n}\n\ninline void Logcat::Log(std::string_view str) {\n    if (verbose_) {\n        fprintf(verbose_file_.get(), \"%.*s\", static_cast<int>(str.size()), str.data());\n        fflush(verbose_file_.get());\n    }\n    fprintf(modules_file_.get(), \"%.*s\", static_cast<int>(str.size()), str.data());\n    fflush(modules_file_.get());\n}\n\nvoid Logcat::OnCrash(int err) {\n    using namespace std::string_literals;\n    constexpr size_t max_restart_logd_wait = 1U << 10;\n    static size_t kLogdCrashCount = 0;\n    static size_t kLogdRestartWait = 1 << 3;\n    if (++kLogdCrashCount >= kLogdRestartWait) {\n        Log(\"\\nLogd crashed too many times, trying manually start...\\n\");\n        __system_property_set(\"ctl.restart\", \"logd\");\n        if (kLogdRestartWait < max_restart_logd_wait) {\n            kLogdRestartWait <<= 1;\n        } else {\n            kLogdCrashCount = 0;\n        }\n    } else {\n        Log(\"\\nLogd maybe crashed (err=\"s + strerror(err) + \"), retrying in 1s...\\n\");\n    }\n\n    std::this_thread::sleep_for(1s);\n}\n\nvoid Logcat::ProcessBuffer(struct log_msg *buf) {\n    AndroidLogEntry entry;\n    if (android_log_processLogBuffer(&buf->entry, &entry) < 0) return;\n\n    entry.tagLen--;\n\n    std::string_view tag(entry.tag, entry.tagLen);\n    bool shortcut = false;\n    if (tag == \"LSPosed-Bridge\"sv || tag == \"XSharedPreferences\"sv || tag == \"LSPosedContext\")\n        [[unlikely]] {\n        modules_print_count_ += PrintLogLine(entry, modules_file_.get());\n        shortcut = true;\n    }\n    if (verbose_ && (shortcut || buf->id() == log_id::LOG_ID_CRASH || entry.pid == my_pid_ ||\n                     tag == \"APatchD\"sv || tag == \"Dobby\"sv || tag.starts_with(\"dex2oat\"sv) ||\n                     tag == \"KernelSU\"sv || tag == \"LSPlant\"sv || tag == \"LSPlt\"sv ||\n                     tag.starts_with(\"LSPosed\"sv) || tag == \"Magisk\"sv || tag == \"SELinux\"sv ||\n                     tag == \"TEESimulator\"sv || tag.starts_with(\"Vector\"sv) ||\n                     tag.starts_with(\"zygisk\"sv))) [[unlikely]] {\n        verbose_print_count_ += PrintLogLine(entry, verbose_file_.get());\n    }\n    if (entry.pid == my_pid_ && tag == \"LSPosedLogcat\"sv) [[unlikely]] {\n        std::string_view msg(entry.message, entry.messageLen);\n        if (msg == \"!!start_verbose!!\"sv) {\n            verbose_ = true;\n            verbose_print_count_ += PrintLogLine(entry, verbose_file_.get());\n        } else if (msg == \"!!stop_verbose!!\"sv) {\n            verbose_ = false;\n        } else if (msg == \"!!refresh_modules!!\"sv) {\n            RefreshFd(false);\n        } else if (msg == \"!!refresh_verbose!!\"sv) {\n            RefreshFd(true);\n        } else if (msg == \"!!start_watchdog!!\"sv) {\n            if (!enable_watchdog) StartLogWatchDog();\n            enable_watchdog = true;\n            enable_watchdog.notify_one();\n        } else if (msg == \"!!stop_watchdog!!\"sv) {\n            enable_watchdog = false;\n            enable_watchdog.notify_one();\n            std::system(\"resetprop -p --delete persist.logd.size\");\n            std::system(\"resetprop -p --delete persist.logd.size.crash\");\n            std::system(\"resetprop -p --delete persist.logd.size.main\");\n            std::system(\"resetprop -p --delete persist.logd.size.system\");\n\n            // Terminate the watchdog thread by exiting __system_property_wait firs firstt\n            std::system(\"setprop persist.log.tag V\");\n            std::system(\"resetprop -p --delete persist.log.tag\");\n        }\n    }\n}\n\nvoid Logcat::StartLogWatchDog() {\n    constexpr static auto kLogdSizeProp = \"persist.logd.size\"sv;\n    constexpr static auto kLogdTagProp = \"persist.log.tag\"sv;\n    constexpr static auto kLogdCrashSizeProp = \"persist.logd.size.crash\"sv;\n    constexpr static auto kLogdMainSizeProp = \"persist.logd.size.main\"sv;\n    constexpr static auto kLogdSystemSizeProp = \"persist.logd.size.system\"sv;\n    constexpr static long kErr = -1;\n    std::thread watchdog([this] {\n        Log(\"[LogWatchDog started]\\n\");\n        while (true) {\n            enable_watchdog.wait(false);  // Blocking current thread until enable_watchdog is true;\n            auto logd_size = GetByteProp(kLogdSizeProp);\n            auto logd_tag = GetStrProp(kLogdTagProp);\n            auto logd_crash_size = GetByteProp(kLogdCrashSizeProp);\n            auto logd_main_size = GetByteProp(kLogdMainSizeProp);\n            auto logd_system_size = GetByteProp(kLogdSystemSizeProp);\n            Log(\"[LogWatchDog running] log.tag: \" + logd_tag +\n                \"; logd.[default, crash, main, system].size: [\" + std::to_string(logd_size) + \",\" +\n                std::to_string(logd_crash_size) + \",\" + std::to_string(logd_main_size) + \",\" +\n                std::to_string(logd_system_size) + \"]\\n\");\n            if (!logd_tag.empty() ||\n                !((logd_crash_size == kErr && logd_main_size == kErr && logd_system_size == kErr &&\n                   logd_size != kErr && logd_size >= kLogBufferSize) ||\n                  (logd_crash_size != kErr && logd_crash_size >= kLogBufferSize &&\n                   logd_main_size != kErr && logd_main_size >= kLogBufferSize &&\n                   logd_system_size != kErr && logd_system_size >= kLogBufferSize))) {\n                SetIntProp(kLogdSizeProp, std::max(kLogBufferSize, logd_size));\n                SetIntProp(kLogdCrashSizeProp, std::max(kLogBufferSize, logd_crash_size));\n                SetIntProp(kLogdMainSizeProp, std::max(kLogBufferSize, logd_main_size));\n                SetIntProp(kLogdSystemSizeProp, std::max(kLogBufferSize, logd_system_size));\n                SetStrProp(kLogdTagProp, \"\");\n                SetStrProp(\"ctl.start\", \"logd-reinit\");\n            }\n            const auto *pi = __system_property_find(kLogdTagProp.data());\n            uint32_t serial = 0;\n            if (pi != nullptr) {\n                __system_property_read_callback(\n                    pi, [](auto *c, auto, auto, auto s) { *reinterpret_cast<uint32_t *>(c) = s; },\n                    &serial);\n            }\n            if (!__system_property_wait(pi, serial, &serial, nullptr)) break;\n            if (pi != nullptr) {\n                if (enable_watchdog) {\n                    Log(\"\\nProp persist.log.tag changed, resetting log settings\\n\");\n                } else {\n                    break;  // End current thread as expected\n                }\n            } else {\n                // log tag prop was not found; to avoid frequently trigger wait, sleep for a while\n                std::this_thread::sleep_for(1s);\n            }\n        }\n        Log(\"[LogWatchDog stopped]\\n\");\n    });\n    pthread_setname_np(watchdog.native_handle(), \"watchdog\");\n    watchdog.detach();\n}\n\nvoid Logcat::Run() {\n    constexpr size_t tail_after_crash = 10U;\n    size_t tail = 0;\n    RefreshFd(true);\n    RefreshFd(false);\n\n    while (true) {\n        std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{\n            android_logger_list_alloc(0, tail, 0), &android_logger_list_free};\n        tail = tail_after_crash;\n\n        for (log_id id : {LOG_ID_MAIN, LOG_ID_CRASH}) {\n            auto *logger = android_logger_open(logger_list.get(), id);\n            if (logger == nullptr) continue;\n            if (auto size = android_logger_get_log_size(logger);\n                size >= 0 && static_cast<size_t>(size) < kLogBufferSize) {\n                android_logger_set_log_size(logger, kLogBufferSize);\n            }\n        }\n\n        struct log_msg msg{};\n\n        while (true) {\n            if (android_logger_list_read(logger_list.get(), &msg) <= 0) [[unlikely]]\n                break;\n\n            ProcessBuffer(&msg);\n\n            if (verbose_print_count_ >= kMaxLogSize) [[unlikely]]\n                RefreshFd(true);\n            if (modules_print_count_ >= kMaxLogSize) [[unlikely]]\n                RefreshFd(false);\n        }\n\n        OnCrash(errno);\n    }\n}\n\nextern \"C\" JNIEXPORT void JNICALL\n// NOLINTNEXTLINE\nJava_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz) {\n    jclass clazz = env->GetObjectClass(thiz);\n    jmethodID method = env->GetMethodID(clazz, \"refreshFd\", \"(Z)I\");\n    Logcat logcat(env, thiz, method);\n    logcat.Run();\n}\n"
  },
  {
    "path": "daemon/src/main/jni/logcat.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <android/log.h>\n\n#define NS_PER_SEC 1000000000L\n#define MS_PER_NSEC 1000000\n#define LOGGER_ENTRY_MAX_LEN (5 * 1024)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct AndroidLogEntry_t {\n    time_t tv_sec;\n    long tv_nsec;\n    android_LogPriority priority;\n    int32_t uid;\n    int32_t pid;\n    int32_t tid;\n    const char *tag;\n    size_t tagLen;\n    size_t messageLen;\n    const char *message;\n} AndroidLogEntry;\n\nstruct logger_entry {\n    uint16_t len;      /* length of the payload */\n    uint16_t hdr_size; /* sizeof(struct logger_entry) */\n    int32_t pid;       /* generating process's pid */\n    uint32_t tid;      /* generating process's tid */\n    uint32_t sec;      /* seconds since Epoch */\n    uint32_t nsec;     /* nanoseconds */\n    uint32_t lid;      /* log id of the payload, bottom 4 bits currently */\n    uint32_t uid;      /* generating process's uid */\n};\n\nstruct log_msg {\n    union alignas(4) {\n        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];\n        struct logger_entry entry;\n    };\n#ifdef __cplusplus\n    log_id_t id() {\n        return static_cast<log_id_t>(entry.lid);\n    }\n#endif\n};\nstruct logger;\nstruct logger_list;\n\nlong android_logger_get_log_size(struct logger* logger);\nint android_logger_set_log_size(struct logger *logger, unsigned long size);\nstruct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid);\nvoid android_logger_list_free(struct logger_list *logger_list);\nint android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg);\nstruct logger *android_logger_open(struct logger_list *logger_list, log_id_t id);\nint android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry);\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "daemon/src/main/jni/logging.h",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\n#ifndef _LOGGING_H\n#define _LOGGING_H\n\n#include <android/log.h>\n#include <errno.h>\n\n#ifndef LOG_TAG\n#define LOG_TAG    \"LSPosed\"\n#endif\n\n#ifdef LOG_DISABLED\n#define LOGD(...) 0\n#define LOGV(...) 0\n#define LOGI(...) 0\n#define LOGW(...) 0\n#define LOGE(...) 0\n#else\n#ifndef NDEBUG\n#define LOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, \"%s:%d#%s\" \": \" fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__)\n#define LOGV(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, \"%s:%d#%s\" \": \" fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__)\n#else\n#define LOGD(...) 0\n#define LOGV(...) 0\n#endif\n#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)\n#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)\n#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)\n#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)\n#define PLOGE(fmt, args...) LOGE(fmt \" failed with %d: %s\", ##args, errno, strerror(errno))\n#endif\n\n#endif // _LOGGING_H\n"
  },
  {
    "path": "daemon/src/main/jni/obfuscation.cpp",
    "content": "#include \"obfuscation.h\"\n\n#include <android/sharedmem.h>\n#include <android/sharedmem_jni.h>\n#include <fcntl.h>\n#include <jni.h>\n#include <slicer/dex_utf8.h>\n#include <slicer/reader.h>\n#include <slicer/writer.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <map>\n#include <mutex>\n#include <random>\n#include <string>\n#include <string_view>\n#include <utils/jni_helper.hpp>\n\nnamespace {\n\nstd::once_flag init_flag;\n\nstd::map<std::string, std::string> signatures = {\n    {\"Lde/robv/android/xposed/\", \"\"},    {\"Landroid/app/AndroidApp\", \"\"},\n    {\"Landroid/content/res/XRes\", \"\"},   {\"Landroid/content/res/XModule\", \"\"},\n    {\"Lorg/matrix/vector/core/\", \"\"},    {\"Lorg/matrix/vector/nativebridge/\", \"\"},\n    {\"Lorg/matrix/vector/service/\", \"\"},\n};\n\njclass class_file_descriptor = nullptr;\njmethodID method_file_descriptor_ctor = nullptr;\n\njclass class_shared_memory = nullptr;\njmethodID method_shared_memory_ctor = nullptr;\n\n}  // anonymous namespace\n\n// Converts Dex signatures to Java format.\n// Trailing slashes are translated to dots, which correctly aligns with\n// Java's string matching expectations for package prefixes.\nstatic std::string to_java(const std::string &signature) {\n    std::string java(signature, 1);\n    std::replace(java.begin(), java.end(), '/', '.');\n    return java;\n}\n\nstatic void ensureInitialized(JNIEnv *env) {\n    // Thread-safe one-time initialization\n    std::call_once(init_flag, [&]() {\n        LOGD(\"ObfuscationManager.init\");\n\n        if (auto file_descriptor = lsplant::JNI_FindClass(env, \"java/io/FileDescriptor\")) {\n            class_file_descriptor =\n                static_cast<jclass>(lsplant::JNI_NewGlobalRef(env, file_descriptor));\n        } else\n            return;\n\n        method_file_descriptor_ctor =\n            lsplant::JNI_GetMethodID(env, class_file_descriptor, \"<init>\", \"(I)V\");\n\n        if (auto shared_memory = lsplant::JNI_FindClass(env, \"android/os/SharedMemory\")) {\n            class_shared_memory =\n                static_cast<jclass>(lsplant::JNI_NewGlobalRef(env, shared_memory));\n        } else\n            return;\n\n        method_shared_memory_ctor = lsplant::JNI_GetMethodID(env, class_shared_memory, \"<init>\",\n                                                             \"(Ljava/io/FileDescriptor;)V\");\n\n        auto regen = [](std::string_view original_signature) {\n            static constexpr auto chrs = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\n            thread_local static std::mt19937 rg{std::random_device{}()};\n            thread_local static std::uniform_int_distribution<std::string::size_type> pick(\n                0, strlen(chrs) - 1);\n            thread_local static std::uniform_int_distribution<std::string::size_type> choose_slash(\n                0, 10);\n\n            std::string out;\n            size_t length = original_signature.size();\n            out.reserve(length);\n            out += \"L\";\n\n            for (size_t i = 1; i < length - 1; i++) {\n                if (choose_slash(rg) > 8 &&  // 20% chance for a slash\n                    out.back() != '/' &&     // Avoid consecutive slashes\n                    i != 1 &&                // No slash immediately after 'L'\n                    i != length - 2) {       // No slash right before the end\n                    out += \"/\";\n                } else {\n                    out += chrs[pick(rg)];\n                }\n            }\n\n            // Respect the original termination character type to prevent\n            if (original_signature.back() == '/') {\n                out += \"/\";\n            } else {\n                out += chrs[pick(rg)];\n            }\n\n            if (out.length() != original_signature.length()) {\n                LOGE(\"Length mismatch! Org: %zu vs New: %zu. '%s' -> '%s'\",\n                     original_signature.length(), out.length(),\n                     std::string(original_signature).c_str(), out.c_str());\n            }\n\n            return out;\n        };\n\n        for (auto &i : signatures) {\n            i.second = regen(i.first);\n            LOGD(\"%s => %s\", i.first.c_str(), i.second.c_str());\n        }\n\n        LOGD(\"ObfuscationManager init successfully\");\n    });\n}\n\nstatic jobject stringMapToJavaHashMap(JNIEnv *env, const std::map<std::string, std::string> &map) {\n    jclass mapClass = env->FindClass(\"java/util/HashMap\");\n    if (mapClass == nullptr) return nullptr;\n\n    jmethodID init = env->GetMethodID(mapClass, \"<init>\", \"()V\");\n    jobject hashMap = env->NewObject(mapClass, init);\n    jmethodID put = env->GetMethodID(mapClass, \"put\",\n                                     \"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\");\n\n    for (const auto &[key, value] : map) {\n        jstring keyJava = env->NewStringUTF(key.c_str());\n        jstring valueJava = env->NewStringUTF(value.c_str());\n\n        env->CallObjectMethod(hashMap, put, keyJava, valueJava);\n\n        env->DeleteLocalRef(keyJava);\n        env->DeleteLocalRef(valueJava);\n    }\n\n    jobject hashMapGlobal = env->NewGlobalRef(hashMap);\n    env->DeleteLocalRef(hashMap);\n    env->DeleteLocalRef(mapClass);\n\n    return hashMapGlobal;\n}\n\nextern \"C\" JNIEXPORT jobject JNICALL Java_org_lsposed_lspd_service_ObfuscationManager_getSignatures(\n    JNIEnv *env, [[maybe_unused]] jclass clazz) {\n    ensureInitialized(env);\n\n    static jobject signatures_jni = nullptr;\n    static std::once_flag jni_map_flag;\n\n    // Thread-safe, one-time JNI HashMap translation\n    std::call_once(jni_map_flag, [&]() {\n        std::map<std::string, std::string> signatures_java;\n        for (const auto &i : signatures) {\n            signatures_java[to_java(i.first)] = to_java(i.second);\n        }\n        signatures_jni = stringMapToJavaHashMap(env, signatures_java);\n    });\n\n    return signatures_jni;\n}\n\nstatic int obfuscateDexBuffer(const void *dex_data, size_t size) {\n    // LOGD(\"obfuscateDexBuffer: dex_data=%p, size=%zu\", dex_data, size);\n    dex::Reader reader{reinterpret_cast<const dex::u1 *>(dex_data), size};\n    reader.CreateFullIr();\n    auto ir = reader.GetIr();\n\n    LOGD(\"Mutating strings in-place\");\n    // Mutate strings in-place.\n    for (auto &i : ir->strings) {\n        const char *s = i->c_str();\n        for (const auto &signature : signatures) {\n            char *p = const_cast<char *>(strstr(s, signature.first.c_str()));\n            if (p) memcpy(p, signature.second.c_str(), signature.first.length());\n        }\n    }\n\n    dex::Writer writer(ir);\n    size_t new_size;\n    DexAllocator allocator;\n\n    // CreateImage calls allocator.Allocate()\n    auto *image = writer.CreateImage(&allocator, &new_size);\n    LOGD(\"writer.CreateImage returned: %p\", image);\n\n    return allocator.GetFd();\n}\n\nextern \"C\" JNIEXPORT jobject JNICALL Java_org_lsposed_lspd_service_ObfuscationManager_obfuscateDex(\n    JNIEnv *env, [[maybe_unused]] jclass clazz, jobject memory) {\n    ensureInitialized(env);\n\n    int fd = ASharedMemory_dupFromJava(env, memory);\n    if (fd < 0) return nullptr;\n\n    auto size = ASharedMemory_getSize(fd);\n    LOGD(\"obfuscateDex: fd=%d, size=%zu\", fd, size);\n\n    // CRITICAL: We MUST use MAP_SHARED here, not MAP_PRIVATE.\n    // 1. Android's SharedMemory is backed by purely virtual IPC buffers (ashmem/memfd).\n    //    If we use MAP_PRIVATE, the kernel attempts to create a Copy-On-Write snapshot.\n    //    Because the Java side just populated this virtual buffer and immediately passed\n    //    it to JNI, mapping it MAP_PRIVATE often results in mapping unpopulated zero-pages,\n    //    which causes Slicer to read a corrupted/empty header and abort.\n    // 2. Using MAP_SHARED gives us direct pointers to the exact physical memory pages\n    //    populated by Java.\n    // 3. ZERO-COPY ARCHITECTURE: Because Slicer's IR holds direct pointers to this mapped\n    //    memory, mutating strings in-place (via const_cast) instantly updates the IR\n    //    without allocating new memory. Since the Java caller discards the original\n    //    SharedMemory buffer anyway, this in-place mutation is completely safe and highly\n    //    efficient.\n    void *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n    if (mem == MAP_FAILED) {\n        LOGE(\"Failed to map input dex\");\n        close(fd);\n        return nullptr;\n    }\n\n    // Process the DEX and obtain a new file descriptor for the output\n    int new_fd = obfuscateDexBuffer(mem, size);\n\n    // Safely unmap and close the input buffer mapping\n    munmap(mem, size);\n    close(fd);\n\n    if (new_fd < 0) {\n        LOGE(\"Obfuscation failed to create new dex buffer\");\n        return nullptr;\n    }\n\n    // Construct new SharedMemory object around the new_fd\n    auto java_fd =\n        lsplant::JNI_NewObject(env, class_file_descriptor, method_file_descriptor_ctor, new_fd);\n    auto java_sm =\n        lsplant::JNI_NewObject(env, class_shared_memory, method_shared_memory_ctor, java_fd);\n\n    return java_sm.release();\n}\n"
  },
  {
    "path": "daemon/src/main/jni/obfuscation.h",
    "content": "#pragma once\n\n#include <android/sharedmem.h>\n#include <slicer/writer.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\n#include \"logging.h\"\n\n// Custom allocator for dex::Writer that creates an ashmem region.\n// Manages the virtual memory mapping lifecycle to prevent memory leaks.\nclass DexAllocator : public dex::Writer::Allocator {\n    void* mapped_mem_ = nullptr;\n    size_t mapped_size_ = 0;\n    int fd_ = -1;\n\npublic:\n    inline void* Allocate(size_t size) override {\n        LOGD(\"DexAllocator: attempting to allocate %zu bytes\", size);\n\n        fd_ = ASharedMemory_create(\"obfuscated_dex\", size);\n        if (fd_ < 0) {\n            // Log the specific error\n            PLOGE(\"DexAllocator: ASharedMemory_create\");\n            return nullptr;\n        }\n\n        mapped_size_ = size;\n        // MAP_SHARED is required for the output buffer so that Slicer's writes\n        // are immediately reflected in the underlying file descriptor.\n        mapped_mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);\n\n        if (mapped_mem_ == MAP_FAILED) {\n            PLOGE(\"DexAllocator: mmap\");\n            close(fd_);\n            fd_ = -1;\n            mapped_mem_ = nullptr;\n        }\n\n        LOGD(\"DexAllocator: success, mapped at %p, fd=%d\", mapped_mem_, fd_);\n        return mapped_mem_;\n    }\n\n    inline void Free(void* ptr) override {\n        if (ptr == mapped_mem_ && mapped_mem_ != nullptr) {\n            munmap(mapped_mem_, mapped_size_);\n            close(fd_);\n            mapped_mem_ = nullptr;\n            fd_ = -1;\n            mapped_size_ = 0;\n        }\n    }\n\n    inline int GetFd() const { return fd_; }\n\n    inline ~DexAllocator() {\n        // Unmap the virtual memory upon destruction to prevent memory leaks.\n        if (mapped_mem_ != nullptr && mapped_mem_ != MAP_FAILED) {\n            munmap(mapped_mem_, mapped_size_);\n        }\n        // Notice: We do NOT close(fd_) here!\n        // The file descriptor is extracted via GetFd() and handed over to Java's SharedMemory,\n        // which assumes lifecycle ownership of it.\n    }\n};\n"
  },
  {
    "path": "daemon/src/main/res/drawable/ic_baseline_block_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "daemon/src/main/res/drawable/ic_baseline_check_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z\"/>\n</vector>\n"
  },
  {
    "path": "daemon/src/main/res/drawable/ic_baseline_close_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" 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": "daemon/src/main/res/drawable/ic_notification.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2022 LSPosed Contributors\n  -->\n\n<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    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17.73,12.02l3.98,-3.98c0.39,-0.39 0.39,-1.02 0,-1.41l-4.34,-4.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.98,3.98L8,2.29C7.8,2.1 7.55,2 7.29,2c-0.25,0 -0.51,0.1 -0.7,0.29L2.25,6.63c-0.39,0.39 -0.39,1.02 0,1.41l3.98,3.98L2.25,16c-0.39,0.39 -0.39,1.02 0,1.41l4.34,4.34c0.39,0.39 1.02,0.39 1.41,0l3.98,-3.98 3.98,3.98c0.2,0.2 0.45,0.29 0.71,0.29 0.26,0 0.51,-0.1 0.71,-0.29l4.34,-4.34c0.39,-0.39 0.39,-1.02 0,-1.41l-3.99,-3.98zM12,9c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM7.29,10.96L3.66,7.34l3.63,-3.63 3.62,3.62 -3.62,3.63zM10,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM12,15c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM14,11c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM16.66,20.34l-3.63,-3.62 3.63,-3.63 3.62,3.62 -3.62,3.63z\" />\n</vector>\n"
  },
  {
    "path": "daemon/src/main/res/values/strings.xml",
    "content": "<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed module is not activated yet</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s has been installed, but is not activated yet</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s has been installed to user %2$s, but is not activated yet</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed module updated</string>\n    <string name=\"xposed_module_updated_notification_content\">%s has been updated, please force stop and restart apps in its scope</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed module updated, system reboot required</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s has been updated, since the scope contains System Framework, required reboot to apply changes</string>\n    <string name=\"module_updated_channel_name\">Module update complete</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed loaded</string>\n    <string name=\"lsposed_running_notification_content\">Tap the notification to open manager</string>\n    <string name=\"xposed_module_request_scope_title\">Scope Request</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s on user %2$s requests to add %3$s to its scope.</string>\n    <string name=\"scope_channel_name\">Scope request</string>\n    <string name=\"scope_approve\">Approve</string>\n    <string name=\"scope_deny\">Deny</string>\n    <string name=\"nerver_ask_again\">Never Ask</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-af/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed module is not activated yet</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s is geïnstalleer, maar is nog nie geaktiveer nie</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s 已為用戶 %2$s 安裝，但尚未激活</string>\n    <string name=\"xposed_module_updated_notification_title\">%d module enabled</string>\n    <string name=\"xposed_module_updated_notification_content\">%s is opgedateer, forseer asseblief stop en herbegin programme binne die omvang daarvan</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed-module is opgedateer, stelselherlaai vereis</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s is opgedateer, aangesien die omvang System Framework bevat, vereis herlaai om veranderinge toe te pas</string>\n    <string name=\"module_updated_channel_name\">Module-opdatering voltooi</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed gelaai</string>\n    <string name=\"lsposed_running_notification_content\">Tik die kennisgewing om bestuurder oop te maak</string>\n    <string name=\"xposed_module_request_scope_title\">Omvang Versoek</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s op gebruiker %2$s versoek om %3$s by sy omvang te voeg.</string>\n    <string name=\"scope_channel_name\">Omvang versoek</string>\n    <string name=\"scope_approve\">Keur goed</string>\n    <string name=\"scope_deny\">Ontken</string>\n    <string name=\"nerver_ask_again\">Moet nooit vra nie</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">وحدة Xposed لم يتم تفعيلها بعد</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s تم التثبيت ولكنه لم يفعل بعد</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s تم تثبيته للمستخدم %2$s ولكن لم يتم تفعيله بعد</string>\n    <string name=\"xposed_module_updated_notification_title\">وحدة Xposed تم تحديثها</string>\n    <string name=\"xposed_module_updated_notification_content\">%s تم تحديثه، يرجى فرض إيقاف وإعادة تشغيل التطبيقات في نطاقه</string>\n    <string name=\"xposed_module_updated_notification_title_system\">تم تحديث وحدة Xposed، مطلوب إعادة تشغيل النظام</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s تم تحديثه، نظراً لأن النطاق يحتوي على إطار النظام، يتطلب إعادة التشغيل لتطبيق التغييرات</string>\n    <string name=\"module_updated_channel_name\">اكتمل تحديث الوحدة</string>\n    <string name=\"status_channel_name\">حالة LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">تم تحميل LSPosed</string>\n    <string name=\"lsposed_running_notification_content\">اضغط على الإشعار لفتح المدير</string>\n    <string name=\"xposed_module_request_scope_title\">Scope Request</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s على %2$s طلبات المستخدم لإضافة %3$s إلى نطاقه.</string>\n    <string name=\"scope_channel_name\">طلب النطاق</string>\n    <string name=\"scope_approve\">اوافق</string>\n    <string name=\"scope_deny\">رفض</string>\n    <string name=\"nerver_ask_again\">لا تسأل أبدا</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-bg/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Модулът Xposed все още не е активиран</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s е инсталиран, но все още не е активиран</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s е инсталиран на потребител %2$s, но все още не е активиран</string>\n    <string name=\"xposed_module_updated_notification_title\">Актуализиран модул Xposed</string>\n    <string name=\"xposed_module_updated_notification_content\">%s е актуализиран, моля, спрете принудително и рестартирайте приложенията в неговия обхват</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Актуализиран модул Xposed, изисква се рестартиране на системата</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s е актуализиран, тъй като обхватът съдържа System Framework, необходимо е рестартиране, за да се приложат промените</string>\n    <string name=\"module_updated_channel_name\">Актуализация на модула е завършена</string>\n    <string name=\"status_channel_name\">Предложен статус на LSP</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed заредени</string>\n    <string name=\"lsposed_running_notification_content\">Докоснете известието, за да отворите мениджъра</string>\n    <string name=\"xposed_module_request_scope_title\">Заявка за обхват</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s при заявки от страна на потребителя %2$s за добавяне на %3$s към неговия обхват.</string>\n    <string name=\"scope_channel_name\">Искане за обхват</string>\n    <string name=\"scope_approve\">Одобряване на</string>\n    <string name=\"scope_deny\">Отказ</string>\n    <string name=\"nerver_ask_again\">Никога не питайте</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-bn/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed মডিউল এখনও সক্রিয় করা হয় নি</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s ইনস্টল করা হয়েছে, কিন্তু এখনও সক্রিয় করা হয়নি</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s ব্যবহারকারী %2$sএ ইনস্টল করা হয়েছে, কিন্তু এখনও সক্রিয় করা হয়নি</string>\n    <string name=\"xposed_module_updated_notification_title\">এক্সপোজড মডিউল আপডেট করা হয়েছে</string>\n    <string name=\"xposed_module_updated_notification_content\">%s আপডেট করা হয়েছে, অনুগ্রহ করে এর সুযোগে অ্যাপগুলিকে জোর করে থামান এবং পুনরায় চালু করুন</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed মডিউল আপডেট করা হয়েছে, সিস্টেম রিবুট প্রয়োজন</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s আপডেট করা হয়েছে, যেহেতু সুযোগে সিস্টেম ফ্রেমওয়ার্ক রয়েছে, পরিবর্তনগুলি প্রয়োগ করার জন্য রিবুট প্রয়োজন</string>\n    <string name=\"module_updated_channel_name\">মডিউল আপডেট সম্পূর্ণ</string>\n    <string name=\"status_channel_name\">LSPপোজড স্ট্যাটাস</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed লোড</string>\n    <string name=\"lsposed_running_notification_content\">ম্যানেজার খুলতে বিজ্ঞপ্তিতে ট্যাপ করুন</string>\n    <string name=\"xposed_module_request_scope_title\">সুযোগ অনুরোধ</string>\n    <string name=\"xposed_module_request_scope_content\">ব্যবহারকারী %2$s এর সুযোগে %3$s যোগ করার অনুরোধে %1$s।</string>\n    <string name=\"scope_channel_name\">সুযোগ অনুরোধ</string>\n    <string name=\"scope_approve\">অনুমোদন করুন</string>\n    <string name=\"scope_deny\">অস্বীকার করুন</string>\n    <string name=\"nerver_ask_again\">কখনো জিজ্ঞাসা করবেন না</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">El mòdul Xposed encara no està activat</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s s\\'ha instal·lat, però encara no està activat</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s s\\'ha instal·lat a l\\'usuari %2$s, però encara no està activat</string>\n    <string name=\"xposed_module_updated_notification_title\">Mòdul Xposed actualitzat</string>\n    <string name=\"xposed_module_updated_notification_content\">%s s\\'ha actualitzat, si us plau, força l\\'aturada i reinici de les aplicacions del seu abast</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Mòdul Xposed actualitzat, cal reiniciar el sistema</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s s\\'ha actualitzat, ja que l\\'abast conté System Framework, cal reiniciar per aplicar els canvis</string>\n    <string name=\"module_updated_channel_name\">S\\'ha completat l\\'actualització del mòdul</string>\n    <string name=\"status_channel_name\">Estat LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed carregat</string>\n    <string name=\"lsposed_running_notification_content\">Toqueu la notificació per obrir el gestor</string>\n    <string name=\"xposed_module_request_scope_title\">Sol·licitud d\\'abast</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s a l\\'usuari %2$s demana afegir %3$s al seu abast.</string>\n    <string name=\"scope_channel_name\">Sol·licitud d\\'abast</string>\n    <string name=\"scope_approve\">Aprovar</string>\n    <string name=\"scope_deny\">Negar</string>\n    <string name=\"nerver_ask_again\">Mai pregunteu</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modul ještě není aktivován</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s byl nainstalován, ale ještě není aktivován</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s byl nainstalován uživateli %2$s, ale ještě není aktivován</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed modul byl aktualizován</string>\n    <string name=\"xposed_module_updated_notification_content\">%s byl aktualizován, prosím, násilně zastavte aplikace a restartujte je</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modul aktualizován, je vyžadován restart systému</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s byl aktualizován, a protože se provedly změny v souvislosti se Systémovým Frameworkem, je vyžadován restart pro aplikaci změn</string>\n    <string name=\"module_updated_channel_name\">Aktualizace modulu dokončena</string>\n    <string name=\"status_channel_name\">Stav LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LPosed načten</string>\n    <string name=\"lsposed_running_notification_content\">Klepnutím na oznámení otevřete správce</string>\n    <string name=\"xposed_module_request_scope_title\">Žádost o rozsah</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s pro uživatele %2$s požaduje přidání %3$s do jeho rozsahu.</string>\n    <string name=\"scope_channel_name\">Žádost o rozsah</string>\n    <string name=\"scope_approve\">Schválit</string>\n    <string name=\"scope_deny\">Odmítnout</string>\n    <string name=\"nerver_ask_again\">Nikdy se neptat</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-da/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modul er endnu ikke aktiveret</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s er blevet installeret, men er ikke aktiveret endnu</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s er blevet installeret på brugeren %2$s, men er endnu ikke aktiveret</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed modul opdateret</string>\n    <string name=\"xposed_module_updated_notification_content\">%s er blevet opdateret, gennemtving stop og genstart apps i dets anvendelsesområde</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modul opdateret, system genstart kræves</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s er blevet opdateret, da anvendelsesområdet indeholder System Framework, krævede genstart for at anvende ændringer</string>\n    <string name=\"module_updated_channel_name\">Modulopdatering afsluttet</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed indlæst</string>\n    <string name=\"lsposed_running_notification_content\">Tryk på meddelelsen for at åbne administratoren</string>\n    <string name=\"xposed_module_request_scope_title\">Anmodning om anvendelsesområde</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s på anmodninger fra brugeren %2$s om at tilføje %3$s til sit anvendelsesområde.</string>\n    <string name=\"scope_channel_name\">Anmodning om anvendelsesområde</string>\n    <string name=\"scope_approve\">Godkend</string>\n    <string name=\"scope_deny\">Afvis</string>\n    <string name=\"nerver_ask_again\">Spørg aldrig</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Das Xposed-Modul ist noch nicht aktiviert</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s wurde installiert, ist aber noch nicht aktiviert</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s wurde unter dem Benutzer %2$s installiert, ist aber noch nicht aktiviert</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed-Modul aktualisiert</string>\n    <string name=\"xposed_module_updated_notification_content\">%s wurde aktualisiert, bitte Stopp erzwingen und die Apps in deren Scope neu starten</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed-Modul aktualisiert, Systemneustart erforderlich</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s wurde aktualisiert, da der Geltungsbereich System-Framework enthält, ist ein Neustart erforderlich, damit die Änderungen übernommen werden</string>\n    <string name=\"module_updated_channel_name\">Modulaktualisierung abgeschlossen</string>\n    <string name=\"status_channel_name\">LSPosed-Status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed geladen</string>\n    <string name=\"lsposed_running_notification_content\">Auf die Benachrichtigung tippen, um den Manager zu öffnen</string>\n    <string name=\"xposed_module_request_scope_title\">Scope-Anfrage</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s von Benutzer %2$s fordert an, %3$s zu seinem Scope-Bereich hinzuzufügen.</string>\n    <string name=\"scope_channel_name\">Scope-Anfrage</string>\n    <string name=\"scope_approve\">Genehmigen</string>\n    <string name=\"scope_deny\">Verweigern</string>\n    <string name=\"nerver_ask_again\">Niemals fragen</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Το Xposed πρόσθετο δεν έχει ενεργοποιηθεί ακόμα</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s έχει εγκατασταθεί, αλλά δεν έχει ενεργοποιηθεί ακόμα</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s έχει εγκατασταθεί στον χρήστη %2$s, αλλά δεν έχει ενεργοποιηθεί ακόμη</string>\n    <string name=\"xposed_module_updated_notification_title\">Το πρόσθετο Xposed ενημερώθηκε</string>\n    <string name=\"xposed_module_updated_notification_content\">%s ενημερώθηκε, παρακαλώ κλείστε εξαναγκαστικά και επανεκκινήστε τις εφαρμογές στο πεδίο εφαρμογής της</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Το πρόσθετο Xposed ενημερώθηκε, απαιτείται επανεκκίνηση συστήματος</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s έχει ενημερωθεί, δεδομένου ότι το πεδίο εφαρμογής περιέχει Πλαίσιο Συστήματος, απαιτείται επανεκκίνηση για να εφαρμοστούν οι αλλαγές</string>\n    <string name=\"module_updated_channel_name\">Η ενημέρωση πρόσθετου ολοκληρώθηκε</string>\n    <string name=\"status_channel_name\">Κατάσταση LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed φορτώθηκε</string>\n    <string name=\"lsposed_running_notification_content\">Πατήστε την ειδοποίηση για άνοιγμα διαχειριστή</string>\n    <string name=\"xposed_module_request_scope_title\">Αίτηση για το πεδίο εφαρμογής</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s στις αιτήσεις του χρήστη %2$s για την προσθήκη του %3$s στο πεδίο εφαρμογής του.</string>\n    <string name=\"scope_channel_name\">Αίτημα πεδίου εφαρμογής</string>\n    <string name=\"scope_approve\">Έγκριση</string>\n    <string name=\"scope_deny\">Άρνηση</string>\n    <string name=\"nerver_ask_again\">Ποτέ μην ρωτάς</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">El módulo Xposed aún no está activado</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s ha sido instalado, pero aún no está activado</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s ha sido instalado al usuario %2$s, pero no está activado todavía</string>\n    <string name=\"xposed_module_updated_notification_title\">Módulo Xpose actualizado</string>\n    <string name=\"xposed_module_updated_notification_content\">%s ha sido actualizado, fuerce la detención y el reinicio de las aplicaciones en su alcance</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Módulo Xposed actualizado, es necesario reiniciar el sistema</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s ha sido actualizado, ya que el ámbito contiene la estructura del sistema, requiere reiniciar para aplicar cambios</string>\n    <string name=\"module_updated_channel_name\">Módulo de actualización completo</string>\n    <string name=\"status_channel_name\">LSPosición de estado</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed cargado</string>\n    <string name=\"lsposed_running_notification_content\">Toca la notificación para abrir el gestor</string>\n    <string name=\"xposed_module_request_scope_title\">Solicitud de alcance</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s cuando el usuario %2$s solicita añadir %3$s a su ámbito.</string>\n    <string name=\"scope_channel_name\">Solicitud de alcance</string>\n    <string name=\"scope_approve\">Aprobar</string>\n    <string name=\"scope_deny\">Denegar</string>\n    <string name=\"nerver_ask_again\">Nunca preguntes</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-et/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed moodul ei ole aktiveeritud</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s on paigaldatud, kuid ei ole aktiveeritud</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s on paigaldatud kasutajale %2$s, kuid ei ole aktiveeritud</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed moodul uuendatud</string>\n    <string name=\"xposed_module_updated_notification_content\">%s on uuendatud, siis peatage ja taaskäivitage rakendused, mis kuuluvad selle kohaldamisalasse.</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed moodul uuendatud, süsteemi taaskäivitamine vajalik</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s on uuendatud, kuna reguleerimisala sisaldab System Framework, vajalik taaskäivitamine, et rakendada muudatusi</string>\n    <string name=\"module_updated_channel_name\">Mooduli uuendamine lõpetatud</string>\n    <string name=\"status_channel_name\">LSPosedi staatus</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed laaditud</string>\n    <string name=\"lsposed_running_notification_content\">Halduri avamiseks puudutage märguannet</string>\n    <string name=\"xposed_module_request_scope_title\">Ulatuse Taotlus</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s kasutajal %2$s taotleb %3$s lisamist oma ulatusse.</string>\n    <string name=\"scope_channel_name\">Ulatuse taotlus</string>\n    <string name=\"scope_approve\">Kinnita</string>\n    <string name=\"scope_deny\">Keela</string>\n    <string name=\"nerver_ask_again\">Ära Enam Küsi</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-fa/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">ماژول Xposed هنوز فعال نشده است</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s نصب شده اما هنوز فعال نشده است</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s برای کاربر %2$s نصب شده اما هنوز فعال نشده است</string>\n    <string name=\"xposed_module_updated_notification_title\">ماژول Xposed به‌روزرسانی شد</string>\n    <string name=\"xposed_module_updated_notification_content\">%s به‌روزرسانی شده است، لطفاً برنامه‌های مربوطه را به‌زور متوقف و مجدداً راه‌اندازی کنید</string>\n    <string name=\"xposed_module_updated_notification_title_system\">ماژول Xposed به‌روزرسانی شد، نیاز به راه‌اندازی مجدد سیستم</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s به‌روزرسانی شده است؛ از آنجا که محدوده شامل چارچوب سیستم است، برای اعمال تغییرات نیاز به راه‌اندازی مجدد سیستم است</string>\n    <string name=\"module_updated_channel_name\">به‌روزرسانی ماژول کامل شد</string>\n    <string name=\"status_channel_name\">وضعیت LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed بارگذاری شد</string>\n    <string name=\"lsposed_running_notification_content\">برای باز کردن مدیر روی اعلان ضربه بزنید</string>\n    <string name=\"xposed_module_request_scope_title\">درخواست محدوده</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s روی کاربر %2$s درخواست افزودن %3$s به محدوده خود را دارد.</string>\n    <string name=\"scope_channel_name\">درخواست محدوده</string>\n    <string name=\"scope_approve\">تأیید</string>\n    <string name=\"scope_deny\">رد</string>\n    <string name=\"nerver_ask_again\">هرگز نپرس</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-fi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed moduuli ei ole vielä aktivoitu</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s on asennettu, mutta sitä ei ole vielä aktivoitu.</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s on asennettu käyttäjälle %2$s, mutta sitä ei ole vielä aktivoitu.</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed moduuli päivitetty</string>\n    <string name=\"xposed_module_updated_notification_content\">%s on päivitetty, paina pysäytä ja käynnistä sovellukset uudelleen sen laajuudessa</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed moduuli päivitetty, järjestelmän uudelleenkäynnistys vaaditaan</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s on päivitetty, koska soveltamisala sisältää järjestelmän kehyksen, vaaditaan uudelleenkäynnistys muutosten käyttöön</string>\n    <string name=\"module_updated_channel_name\">Moduulin päivitys valmis</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed ladattu</string>\n    <string name=\"lsposed_running_notification_content\">Avaa manager napauttamalla ilmoitusta</string>\n    <string name=\"xposed_module_request_scope_title\">Soveltamisalaa koskeva pyyntö</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s käyttäjän %2$s pyynnöistä lisätä %3$s sen toimialueeseen.</string>\n    <string name=\"scope_channel_name\">Laajuuspyyntö</string>\n    <string name=\"scope_approve\">Hyväksy</string>\n    <string name=\"scope_deny\">Kiellä</string>\n    <string name=\"nerver_ask_again\">Älä koskaan kysy</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Le module LSPosed n\\’est pas encore actif</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s a été installé, mais n\\'a pas été encore activé</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s a été installé pour l\\'utilisateur %2$s, mais n\\'a pas été encore activé</string>\n    <string name=\"xposed_module_updated_notification_title\">Module Xposed mis à jour</string>\n    <string name=\"xposed_module_updated_notification_content\">%s a été mis à jour, merci de forcer l\\’arrêt ou de redémarrer les applis dans leurs champs d\\’application</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Module Xposed mis à jour, redémarrage du système requis</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s a été mis à jour, étant donné que le champ d\\'application est étendu au sous système, un redémarrage est nécessaire pour appliquer les changements</string>\n    <string name=\"module_updated_channel_name\">Mise à jour du module terminée</string>\n    <string name=\"status_channel_name\">Statut LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed chargé</string>\n    <string name=\"lsposed_running_notification_content\">Appuyer sur la notification pour ouvrir le gestionnaire</string>\n    <string name=\"xposed_module_request_scope_title\">Demande d\\'Extension de Portée</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s sur l\\'utilisateur %2$s demande d\\'ajouter %3$s à son périmètre d\\'action.</string>\n    <string name=\"scope_channel_name\">Demande d\\'Extension de Portée</string>\n    <string name=\"scope_approve\">Approuver</string>\n    <string name=\"scope_deny\">Refuser</string>\n    <string name=\"nerver_ask_again\">Ne jamais demander</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-hi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">एक्सपोज़ड मॉड्यूल अभी तक सक्रिय नहीं है</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s इंस्टॉल कर दिया गया है, लेकिन अब तक एक्टिवेट नही किया गया</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s को %2$s यूजर में इंस्टॉल कर दिया गया है, लेकिन अब तक एक्टिवेट नही किया गया है</string>\n    <string name=\"xposed_module_updated_notification_title\">एक्सपोज़ड मॉड्यूल अपडेट किया गया</string>\n    <string name=\"xposed_module_updated_notification_content\">%s अपडेट कर दिया गया है, कृपया बलपूर्वक रोकें और इसके दायरे में ऐप्स को पुनरारंभ करें</string>\n    <string name=\"xposed_module_updated_notification_title_system\">एक्सपोज़ड मॉड्यूल अपडेट किया गया, सिस्टम रीबूट की आवश्यकता है</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s को अपडेट कर दिया गया है, क्योंकि स्कोप में सिस्टम फ्रेमवर्क है, परिवर्तनों को लागू करने के लिए रीबूट की आवश्यकता है</string>\n    <string name=\"module_updated_channel_name\">मॉड्यूल अद्यतन पूर्ण</string>\n    <string name=\"status_channel_name\">एलएसपोस्ड स्थिति</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed लोड किया गया</string>\n    <string name=\"lsposed_running_notification_content\">मैनेजर खोलने के लिए नोटिफिकेशन पर टैप करें</string>\n    <string name=\"xposed_module_request_scope_title\">गुंजाइश अनुरोध</string>\n    <string name=\"xposed_module_request_scope_content\">उपयोगकर्ता %2$s पर %1$s इसके दायरे में %3$s जोड़ने का अनुरोध करता है।</string>\n    <string name=\"scope_channel_name\">दायरा अनुरोध</string>\n    <string name=\"scope_approve\">मंज़ूरी देना</string>\n    <string name=\"scope_deny\">अस्वीकार करना</string>\n    <string name=\"nerver_ask_again\">कभी मत पूछो</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-hr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modul još nije aktiviran</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s je instaliran, ali još nije aktiviran</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s je instaliran korisniku %2$s, ali još nije aktiviran</string>\n    <string name=\"xposed_module_updated_notification_title\">Modul Xposed ažuriran</string>\n    <string name=\"xposed_module_updated_notification_content\">%s je ažuriran, prisilno zaustavite i ponovno pokrenite aplikacije u njegovom opsegu</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modul je ažuriran, potrebno je ponovno pokretanje sustava</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s je ažuriran, budući da opseg sadrži System Framework, potrebno je ponovno pokretanje za primjenu promjena</string>\n    <string name=\"module_updated_channel_name\">Ažuriranje modula dovršeno</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed je učitan</string>\n    <string name=\"lsposed_running_notification_content\">Dodirnite obavijest da otvorite upravitelja</string>\n    <string name=\"xposed_module_request_scope_title\">Zahtjev za opseg</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s na korisniku %2$s zahtijeva dodavanje %3$s svom opsegu.</string>\n    <string name=\"scope_channel_name\">Zahtjev za opseg</string>\n    <string name=\"scope_approve\">Odobriti</string>\n    <string name=\"scope_deny\">Poreći</string>\n    <string name=\"nerver_ask_again\">Nikad ne pitaj</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-hu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Az Xposed modul még nincs aktiválva</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s telepítve lett, de még nincs aktiválva.</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s telepítve lett a %2$sfelhasználóhoz, de még nincs aktiválva.</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed modul frissítve</string>\n    <string name=\"xposed_module_updated_notification_content\">%s frissítésre került, kérjük, kényszerítse az alkalmazások leállítását és újraindítását a hatókörében.</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modul frissítve, rendszer újraindítás szükséges</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s frissítve lett, mivel a hatókör tartalmazza a System Framework-et, a változások alkalmazásához szükséges újraindítás szükséges.</string>\n    <string name=\"module_updated_channel_name\">A modul frissítése befejeződött</string>\n    <string name=\"status_channel_name\">LSPosed állapot</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed betöltve</string>\n    <string name=\"lsposed_running_notification_content\">Érintse meg az értesítést a menedzser megnyitásához</string>\n    <string name=\"xposed_module_request_scope_title\">Hatókör kérés</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s a felhasználó %2$s kérésére a %3$s hozzáadására a hatóköréhez.</string>\n    <string name=\"scope_channel_name\">Hatókör kérés</string>\n    <string name=\"scope_approve\">Jóváhagyás</string>\n    <string name=\"scope_deny\">Megtagadás</string>\n    <string name=\"nerver_ask_again\">Soha ne kérdezz rá</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-in/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Modul Xposed belum diaktifkan</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s sudah diinstal, tetapi belum diaktifkan</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s telah diinstal ke pengguna %2$s, tetapi belum diaktifkan</string>\n    <string name=\"xposed_module_updated_notification_title\">Modul xposed diperbarui</string>\n    <string name=\"xposed_module_updated_notification_content\">%s telah diperbarui, harap paksa berhenti dan mulai ulang aplikasi dalam cakupannya</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Modul Xposed diperbarui, diperlukan memulai ulang sistem</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s telah diperbarui, karena cakupannya berisi Kerangka Sistem, diperlukan mulai ulang untuk menerapkan perubahan</string>\n    <string name=\"module_updated_channel_name\">Pembaruan modul selesai</string>\n    <string name=\"status_channel_name\">Status LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed dimuat</string>\n    <string name=\"lsposed_running_notification_content\">Ketuk notifikasi untuk membuka pengelola</string>\n    <string name=\"xposed_module_request_scope_title\">Permintaan Cakupan</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s pada pengguna %2$s meminta untuk menambahkan %3$s ke dalam cakupannya.</string>\n    <string name=\"scope_channel_name\">Permintaan cakupan</string>\n    <string name=\"scope_approve\">Menyetujui</string>\n    <string name=\"scope_deny\">Menolak</string>\n    <string name=\"nerver_ask_again\">Jangan Pernah Bertanya</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Il modulo Xposed non è ancora attivo</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s è stato installato, ma non è ancora attivo</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s è stato installato sull\\'utente %2$s, ma non è ancora attivo</string>\n    <string name=\"xposed_module_updated_notification_title\">Modulo Xposed aggiornato</string>\n    <string name=\"xposed_module_updated_notification_content\">%s è stato aggiornato, arresta e riavvia le applicazioni per le quali è abilitato</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Modulo Xposed aggiornato, è necessario il riavvio del sistema</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s è stato aggiornato. Poiché è abilitato per il framework di sistema, è necessario riavviare per applicare le modifiche</string>\n    <string name=\"module_updated_channel_name\">Aggiornamento del modulo completato</string>\n    <string name=\"status_channel_name\">Stato LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed caricato</string>\n    <string name=\"lsposed_running_notification_content\">Tocca la notifica per aprire il manager</string>\n    <string name=\"xposed_module_request_scope_title\">Richiesta attivazione</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s sull\\'utente %2$s richiede di aggiungere %3$s alle sue attivazioni.</string>\n    <string name=\"scope_channel_name\">Richiesta attivazione</string>\n    <string name=\"scope_approve\">Approva</string>\n    <string name=\"scope_deny\">Nega</string>\n    <string name=\"nerver_ask_again\">Non chiedere mai</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-iw/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">מודול LSPosed עדיין לא הופעל</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s הותקן, אך אינו מופעל עדיין</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s הותקן למשתמש %2$s, אך אינו מופעל עדיין</string>\n    <string name=\"xposed_module_updated_notification_title\">מודול LSPosed עודכן</string>\n    <string name=\"xposed_module_updated_notification_content\">%s עודכן</string>\n    <string name=\"xposed_module_updated_notification_title_system\">מודול Xposed עודכן, נדרש אתחול המערכת</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s עודכן, מכיוון שההיקף מכיל System Framework, נדרש אתחול כדי להחיל שינויים</string>\n    <string name=\"module_updated_channel_name\">עדכון המודול הושלם</string>\n    <string name=\"status_channel_name\">סטטוס LSPost</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed נטען</string>\n    <string name=\"lsposed_running_notification_content\">הקש על ההודעה כדי לפתוח את המנהל</string>\n    <string name=\"xposed_module_request_scope_title\">Xposed_מודול_מבקש_כותרת_תחום</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s על משתמש %2$s מבקש להוסיף %3$s להיקף שלו.</string>\n    <string name=\"scope_channel_name\">תחום_שם_ערוץ</string>\n    <string name=\"scope_approve\">אישור_תחום</string>\n    <string name=\"scope_deny\">לְהַכּחִישׁ</string>\n    <string name=\"nerver_ask_again\">לעולם אל תשאל</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed モジュールが有効化されていません</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s はインストールされましたが､ 有効化されていません</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s はユーザー %2$s にインストールされましたが､ 有効化されていません</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed モジュールが更新されました</string>\n    <string name=\"xposed_module_updated_notification_content\">%s が更新されました。スコープ内のアプリを強制停止してから再起動してください</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed モジュールが更新されました。システムの再起動が必要です</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s が更新されました。スコープにシステムフレームワークが含まれているため、変更を適用するには再起動が必要です</string>\n    <string name=\"module_updated_channel_name\">モジュールの更新完了通知</string>\n    <string name=\"status_channel_name\">LSPosed のステータス通知</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed の読み込み完了通知</string>\n    <string name=\"lsposed_running_notification_content\">通知をタップしてマネージャーを開きます</string>\n    <string name=\"xposed_module_request_scope_title\">スコープのリクエスト</string>\n    <string name=\"xposed_module_request_scope_content\">ユーザー %2$s の %1$s が %3$s をそのスコープに追加するようリクエストしています。</string>\n    <string name=\"scope_channel_name\">スコープのリクエスト</string>\n    <string name=\"scope_approve\">許可</string>\n    <string name=\"scope_deny\">拒否</string>\n    <string name=\"nerver_ask_again\">再度表示しない</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed 모듈이 아직 활성화되지 않았습니다.</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s이(가) 설치되었지만 아직 활성화되지 않았습니다.</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">사용자 %2$s님에게 %1$s 이(가) 설치되었지만 아직 활성화되지 않았습니다.</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed 모듈 업데이트</string>\n    <string name=\"xposed_module_updated_notification_content\">%s이(가) 업데이트되었습니다.</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed 모듈이 업데이트되었습니다, 재부팅이 필요합니다.</string>\n    <string name=\"xposed_module_updated_notification_content_system\">범위에 시스템 프레임워크가 포함되어 있으므로 %s 이 업데이트되었습니다. 변경 사항을 적용하려면 재부팅해야 합니다.</string>\n    <string name=\"module_updated_channel_name\">모듈 업데이트 완료</string>\n    <string name=\"status_channel_name\">LS포즈 상태</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed 로드됨</string>\n    <string name=\"lsposed_running_notification_content\">알림을 탭하여 관리자 열기</string>\n    <string name=\"xposed_module_request_scope_title\">범위 요청</string>\n    <string name=\"xposed_module_request_scope_content\">사용자 %2$s 의 %1$s 은 해당 범위에 %3$s 을 추가하도록 요청합니다.</string>\n    <string name=\"scope_channel_name\">범위 요청</string>\n    <string name=\"scope_approve\">승인</string>\n    <string name=\"scope_deny\">거부</string>\n    <string name=\"nerver_ask_again\">다시 묻지 않음</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ku/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Modula Xposed hîn nehatiye çalak kirin</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s hatiye saz kirin, lê hîn nehatiye aktîfkirin</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s ji bikarhêner %2$sre hate saz kirin, lê hêj nehatiye çalak kirin</string>\n    <string name=\"xposed_module_updated_notification_title\">Modula Xposed hate nûve kirin</string>\n    <string name=\"xposed_module_updated_notification_content\">%s hate nûve kirin, ji kerema xwe bi zorê sepanan rawestînin û di çarçoveya wê de ji nû ve bidin destpêkirin</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Modula Xposed hate nûve kirin, pêdivî ye ku pergalê ji nû ve dest pê bike</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s hate nûve kirin, ji ber ku çarçove Çarçoveya Pergalê dihewîne, ji bo sepandina guhertinan ji nû ve destpêkirinê hewce dike</string>\n    <string name=\"module_updated_channel_name\">Nûvekirina modulê qediya</string>\n    <string name=\"status_channel_name\">statûya LSP</string>\n    <string name=\"lsposed_running_notification_title\">LSP hate barkirin</string>\n    <string name=\"lsposed_running_notification_content\">Daxuyaniyê bikirtînin da ku rêveberê vekin</string>\n    <string name=\"xposed_module_request_scope_title\">Scope Daxwaza</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s li ser bikarhêner %2$s daxwaz dike ku %3$s li qada xwe zêde bike.</string>\n    <string name=\"scope_channel_name\">Daxwaza Scope</string>\n    <string name=\"scope_approve\">Destûrdan</string>\n    <string name=\"scope_deny\">Înkarkirin</string>\n    <string name=\"nerver_ask_again\">Never Ask</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-lt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modulis dar nėra aktyvuotas</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">\\\"%1$s\\\" buvo įdiegta, tačiau liko dar nesuaktyvuota</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">\\\"%1$s\\\" buvo įdiegta vartotojui \\\"%2$s\\\", tačiau liko dar nesuaktyvuota</string>\n    <string name=\"xposed_module_updated_notification_title\">Atnaujintas Xposed modulis</string>\n    <string name=\"xposed_module_updated_notification_content\">%s buvo atnaujintas, priverstinai sustabdykite ir iš naujo paleiskite jo taikymo srityje esančias programas</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Atnaujintas \\\"Xposed\\\" modulis, reikalingas sistemos perkrovimas</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s buvo atnaujintas, nes srityje yra System Framework, reikalingas perkrovimas, kad būtų galima taikyti pakeitimus</string>\n    <string name=\"module_updated_channel_name\">Modulio atnaujinimas baigtas</string>\n    <string name=\"status_channel_name\">LSPatvirtintas statusas</string>\n    <string name=\"lsposed_running_notification_title\">LSPpateiktas pakrautas</string>\n    <string name=\"lsposed_running_notification_content\">Bakstelėkite pranešimą, kad atidarytumėte tvarkytuvę</string>\n    <string name=\"xposed_module_request_scope_title\">Apimties prašymas</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s pagal naudotojo %2$s užklausas įtraukti %3$s į jo taikymo sritį.</string>\n    <string name=\"scope_channel_name\">Apimties prašymas</string>\n    <string name=\"scope_approve\">Patvirtinti</string>\n    <string name=\"scope_deny\">Atsisakyti</string>\n    <string name=\"nerver_ask_again\">Niekada neklauskite</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">LSPosed module is nog niet geactiveerd</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s is geïnstalleerd, maar nog niet geactiveerd</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s is geïnstalleerd bij gebruiker %2$s, maar is nog niet geactiveerd</string>\n    <string name=\"xposed_module_updated_notification_title\">LSPosed module bijgewerkt</string>\n    <string name=\"xposed_module_updated_notification_content\">%1$s is geupdate</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed-module bijgewerkt, systeem opnieuw opstarten vereist</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s is bijgewerkt, omdat het bereik een systeemkader bevat, moet je opnieuw opstarten om wijzigingen toe te passen</string>\n    <string name=\"module_updated_channel_name\">Module update voltooid</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed geladen</string>\n    <string name=\"lsposed_running_notification_content\">Tik op de melding om manager te openen</string>\n    <string name=\"xposed_module_request_scope_title\">Scope verzoek</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s op gebruiker %2$s verzoeken om %3$s toe te voegen aan het toepassingsgebied.</string>\n    <string name=\"scope_channel_name\">Scope verzoek</string>\n    <string name=\"scope_approve\">Goedkeuren</string>\n    <string name=\"scope_deny\">Weiger</string>\n    <string name=\"nerver_ask_again\">Nooit vragen</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-no/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modul er ikke aktivert enda</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s er installert, men er ikke aktivert ennå</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s har blitt installert til bruker %2$s, men er ikke aktivert ennå</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed modul er oppdatert</string>\n    <string name=\"xposed_module_updated_notification_content\">%s har blitt oppdatert, vennligst tvang-stopp og start apper på nytt i virkeområdet</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modul oppdatert, system-omstart kreves</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s er blitt oppdatert, siden omfanget inneholder systemramme, nødvendig for omstart av endringene</string>\n    <string name=\"module_updated_channel_name\">Moduloppdatering fullført</string>\n    <string name=\"status_channel_name\">LSPosert status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosert lastet</string>\n    <string name=\"lsposed_running_notification_content\">Trykk på varselet for å åpne administrator</string>\n    <string name=\"xposed_module_request_scope_title\">Forespørsel om omfang</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s på bruker %2$s ber om å legge til %3$s i omfanget.</string>\n    <string name=\"scope_channel_name\">Forespørsel om omfang</string>\n    <string name=\"scope_approve\">Vedta</string>\n    <string name=\"scope_deny\">Benekte</string>\n    <string name=\"nerver_ask_again\">Spør aldri</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Moduł Xposed nie jest jeszcze aktywowany</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s został zainstalowany, ale nie jest jeszcze aktywny</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s został zainstalowany na użytkowniku %2$s, ale nie jest jeszcze aktywowany</string>\n    <string name=\"xposed_module_updated_notification_title\">Moduł Xposed zaktualizowany</string>\n    <string name=\"xposed_module_updated_notification_content\">%s został zaktualizowany, wymuś zatrzymanie i ponownie uruchom aplikacje w jego zakresie</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Zaktualizowano moduł Xposed, wymagane ponowne uruchomienie systemu</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s został zaktualizowany, ponieważ zakres zawiera System Framework, wymagany restart aby zastosować zmiany</string>\n    <string name=\"module_updated_channel_name\">Aktualizowanie modułu zakończone</string>\n    <string name=\"status_channel_name\">Status LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed załadowany</string>\n    <string name=\"lsposed_running_notification_content\">Kliknij powiadomienie, by włączyć menadżer</string>\n    <string name=\"xposed_module_request_scope_title\">Żądanie Zakresu</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s w użytkowniku %2$s żąda dodania %3$s do jego zakresu.</string>\n    <string name=\"scope_channel_name\">Żądanie zakresu</string>\n    <string name=\"scope_approve\">Zatwierdź</string>\n    <string name=\"scope_deny\">Odrzuć</string>\n    <string name=\"nerver_ask_again\">Nigdy nie pytaj</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">O módulo ainda não está ativo</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s foi instalado, mas ainda não está ativo</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s foi instalado no usuário %2$s, mas ainda não está ativo</string>\n    <string name=\"xposed_module_updated_notification_title\">Módulo Atualizado</string>\n    <string name=\"xposed_module_updated_notification_content\">%s foi atualizado. Force a parada do módulo e reinicie os apps que estão em seu escopo</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Módulo Atualizado. É necessário reiniciar o sistema</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s foi atualizado. Como o escopo contém o Framework do Sistema, é necessário reiniciar para aplicar as mudanças</string>\n    <string name=\"module_updated_channel_name\">Atualização do módulo concluída</string>\n    <string name=\"status_channel_name\">Estado do LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed carregado</string>\n    <string name=\"lsposed_running_notification_content\">Toque na notificação para abrir o gerenciador</string>\n    <string name=\"xposed_module_request_scope_title\">Pedido de âmbito de aplicação</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s sobre o utilizador %2$s pede para acrescentar %3$s ao seu âmbito de aplicação.</string>\n    <string name=\"scope_channel_name\">Pedido de âmbito de aplicação</string>\n    <string name=\"scope_approve\">Aprovar</string>\n    <string name=\"scope_deny\">Negar</string>\n    <string name=\"nerver_ask_again\">Nunca Pergunte</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">O módulo ainda não está ativo</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s foi instalado, mas ainda não está ativo</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s foi instalado no usuário %2$s, mas ainda não está ativo</string>\n    <string name=\"xposed_module_updated_notification_title\">Módulo atualizado</string>\n    <string name=\"xposed_module_updated_notification_content\">%s foi atualizado. Force a parada e reinicie os apps que estão em seu escopo.</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Módulo atualizado. É necessário reiniciar o sistema.</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s foi atualizado. Como o escopo contém o Framework do Sistema, é necessário reiniciar para aplicar as mudanças.</string>\n    <string name=\"module_updated_channel_name\">Atualização do módulo concluída</string>\n    <string name=\"status_channel_name\">Status do LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed carregado</string>\n    <string name=\"lsposed_running_notification_content\">Toque na notificação para abrir o gerenciador</string>\n    <string name=\"xposed_module_request_scope_title\">Solicitação de escopo</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s do usuário %2$s está solicitando para adicionar %3$s no seu escopo.</string>\n    <string name=\"scope_channel_name\">Solicitação de escopo</string>\n    <string name=\"scope_approve\">Permitir</string>\n    <string name=\"scope_deny\">Negar</string>\n    <string name=\"nerver_ask_again\">Nunca perguntar</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ro/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Modulul Xposed nu este încă activat</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">Modulul %1$s este instalat, dar nu este încă activat</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">Modulul %1$s a fost instalat pentru utilizatorul %2$s, dar nu este încă activat</string>\n    <string name=\"xposed_module_updated_notification_title\">Modulul Xposed a fost actualizat</string>\n    <string name=\"xposed_module_updated_notification_content\">Modulul %s a fost actualizat, vă rugăm să reporniți aplicațiile din cadrul configurației sale</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Modulul Xposed a fost actualizat, este necesară repornirea sistemului</string>\n    <string name=\"xposed_module_updated_notification_content_system\">Modulul %s a fost actualizat. Este necesară repornirea dispozitivului, deoarece Sistemul Android face parte din configurația modulului.</string>\n    <string name=\"module_updated_channel_name\">Actualizarea modulelor este completă</string>\n    <string name=\"status_channel_name\">Stare LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed încărcat</string>\n    <string name=\"lsposed_running_notification_content\">Apăsați pentru a deschide managerul</string>\n    <string name=\"xposed_module_request_scope_title\">Cerere de modificare configurație</string>\n    <string name=\"xposed_module_request_scope_content\">Modulul %1$s, instalat pentru utilizatorul %2$s, dorește să adauge %3$s în configurația sa.</string>\n    <string name=\"scope_channel_name\">Cerere de modificare configurație</string>\n    <string name=\"scope_approve\">Aprobați</string>\n    <string name=\"scope_deny\">Refuzați</string>\n    <string name=\"nerver_ask_again\">Nu afișați din nou</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Модуль Xposed пока не активирован</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s установлен, но пока не активирован</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s установлен (пользователь %2$s), но пока не активирован</string>\n    <string name=\"xposed_module_updated_notification_title\">Модуль Xposed обновлён</string>\n    <string name=\"xposed_module_updated_notification_content\">%s обновлён, выполните остановку приложений в его «охвате» и перезапустите их</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Модуль Xposed обновлён, требуется перезагрузка устройства</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s обновлён; ввиду того, что системный фреймворк (System Framework) в его «охвате», требуется перезагрузка для применения изменений</string>\n    <string name=\"module_updated_channel_name\">Обновление модуля завершено</string>\n    <string name=\"status_channel_name\">Статус LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed загружен</string>\n    <string name=\"lsposed_running_notification_content\">Нажмите уведомление, чтобы открыть LSPosed Manager</string>\n    <string name=\"xposed_module_request_scope_title\">Запрос «охвата»</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s (пользователь %2$s): запрашивается добавление %3$s в «охват».</string>\n    <string name=\"scope_channel_name\">Запрос «охвата»</string>\n    <string name=\"scope_approve\">Принять</string>\n    <string name=\"scope_deny\">Отклонить</string>\n    <string name=\"nerver_ask_again\">Больше не спрашивать</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-si/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed මොඩියුලය තවම සක්‍රිය කර නැත</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s ස්ථාපනය කර ඇත, නමුත් තවමත් සක්රිය කර නැත</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">පරිශීලක %2$sවෙත %1$s ස්ථාපනය කර ඇත, නමුත් තවමත් සක්‍රිය කර නොමැත</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed මොඩියුලය යාවත්කාලීන කරන ලදී</string>\n    <string name=\"xposed_module_updated_notification_content\">%s යාවත්කාලීන කර ඇත, කරුණාකර එහි විෂය පථය තුළ යෙදුම් බලහත්කාරයෙන් නතර කර නැවත ආරම්භ කරන්න</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed මොඩියුලය යාවත්කාලීන කරන ලදි, පද්ධතිය නැවත ආරම්භ කිරීම අවශ්‍යයි</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s යාවත්කාලීන කර ඇත, විෂය පථයේ පද්ධති රාමුව අඩංගු බැවින්, වෙනස්කම් යෙදීමට නැවත පණගැන්වීම අවශ්‍ය වේ</string>\n    <string name=\"module_updated_channel_name\">මොඩියුල යාවත්කාලීන කිරීම සම්පූර්ණයි</string>\n    <string name=\"status_channel_name\">එල්එස්පී තත්ත්වය</string>\n    <string name=\"lsposed_running_notification_title\">LSPposed පටවා ඇත</string>\n    <string name=\"lsposed_running_notification_content\">කළමනාකරු විවෘත කිරීමට දැනුම්දීම තට්ටු කරන්න</string>\n    <string name=\"xposed_module_request_scope_title\">විෂය පථය ඉල්ලීම</string>\n    <string name=\"xposed_module_request_scope_content\">පරිශීලක %2$s හි %1$s එහි විෂය පථයට %3$s එකතු කරන ලෙස ඉල්ලා සිටී.</string>\n    <string name=\"scope_channel_name\">විෂය පථය ඉල්ලීම</string>\n    <string name=\"scope_approve\">අනුමත කරන්න</string>\n    <string name=\"scope_deny\">ප්රතික්ෂේප කරන්න</string>\n    <string name=\"nerver_ask_again\">කවදාවත් අහන්න එපා</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-sk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Modul Xposed ešte nie je aktivovaný</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s bol nainštalovaný, ale ešte nie je aktivovaný</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s bol nainštalovaný na stránke používateľa %2$s, ale ešte nie je aktivovaný.</string>\n    <string name=\"xposed_module_updated_notification_title\">Aktualizovaný modul Xposed</string>\n    <string name=\"xposed_module_updated_notification_content\">%s bola aktualizovaná, vynúťte si zastavenie a reštartovanie aplikácií v jej rozsahu</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Aktualizácia modulu Xposed, potrebný reštart systému</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s bola aktualizovaná, pretože rozsah obsahuje System Framework, potrebný reštart na uplatnenie zmien</string>\n    <string name=\"module_updated_channel_name\">Aktualizácia modulu dokončená</string>\n    <string name=\"status_channel_name\">LSPonúkaný stav</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed naložené</string>\n    <string name=\"lsposed_running_notification_content\">Ťuknutím na oznámenie otvorte správcu</string>\n    <string name=\"xposed_module_request_scope_title\">Žiadosť o rozsah</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s na žiadosti používateľa %2$s o pridanie stránky %3$s do jej rozsahu.</string>\n    <string name=\"scope_channel_name\">Žiadosť o rozsah</string>\n    <string name=\"scope_approve\">Schváliť</string>\n    <string name=\"scope_deny\">Odmietnuť</string>\n    <string name=\"nerver_ask_again\">Nikdy sa nepýtajte</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-sv/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modul är inte aktiverad än</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s har installerats, men är ännu inte aktiverad.</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s har installerats för användaren %2$s, men är ännu inte aktiverad.</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed modul uppdaterad</string>\n    <string name=\"xposed_module_updated_notification_content\">%s har uppdaterats. Tvinga stopp och starta om appar i dess omfattning</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modul uppdaterad, systemomstart krävs</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s har uppdaterats, eftersom omfattningen innehåller Systemramverk, krävs omstart för att tillämpa ändringar</string>\n    <string name=\"module_updated_channel_name\">Uppdatering av modulen slutförd</string>\n    <string name=\"status_channel_name\">LSPosed status</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed laddad</string>\n    <string name=\"lsposed_running_notification_content\">Tryck på meddelandet för att öppna administratören</string>\n    <string name=\"xposed_module_request_scope_title\">Begäran om tillämpningsområde</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s om användaren %2$s begär att %3$s ska läggas till i dess räckvidd.</string>\n    <string name=\"scope_channel_name\">Begäran om tillämpningsområde</string>\n    <string name=\"scope_approve\">Godkänna</string>\n    <string name=\"scope_deny\">Förneka</string>\n    <string name=\"nerver_ask_again\">Fråga aldrig</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-th/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">โมดูล Xposed ยังไม่ได้เปิดใช้งาน</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s ถูกติดตั้งแล้ว แต่ยังไม่ได้เปิดใช้งาน</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">ติดตั้ง %1$s ให้กับผู้ใช้แล้ว %2$s แต่ยังไม่ได้เปิดใช้งาน</string>\n    <string name=\"xposed_module_updated_notification_title\">โมดูล Xposed อัปเดตแล้ว</string>\n    <string name=\"xposed_module_updated_notification_content\">%s ได้รับการอัปเดตแล้ว โปรดบังคับหยุดและรีสตาร์ทแอปที่อยู่ใน Scope.</string>\n    <string name=\"xposed_module_updated_notification_title_system\">อัปเดตโมดูล Xposed จำเป็นต้องรีสตาร์ทเครื่อง</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s ได้รับการอัปเดตแล้ว เนื่องจาก Scope มี System Framework จึงจำเป็นต้องรีสตาร์ทเครื่องเพื่อใช้การเปลี่ยนแปลง</string>\n    <string name=\"module_updated_channel_name\">การอัปเดตโมดูลเสร็จสมบูรณ์</string>\n    <string name=\"status_channel_name\">สถานะ LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed โหลดแล้ว</string>\n    <string name=\"lsposed_running_notification_content\">แตะการแจ้งเตือนเพื่อเปิดตัวจัดการ</string>\n    <string name=\"xposed_module_request_scope_title\">คำขอ Scope</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s ผูัใช้นี้ %2$s ขอให้เพิ่ม %3$s ใน List ของ scope.</string>\n    <string name=\"scope_channel_name\">คำขอ Scope.</string>\n    <string name=\"scope_approve\">อนุมัติ</string>\n    <string name=\"scope_deny\">ปฎิเสธ</string>\n    <string name=\"nerver_ask_again\">ไม่เคยถาม</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed modülü henüz aktif değil!</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s kuruldu, ancak henüz etkinleştirilmedi</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s, kullanıcıya %2$syüklendi, ancak henüz etkinleştirilmedi</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed modülü güncellendi</string>\n    <string name=\"xposed_module_updated_notification_content\">%s güncellendi, lütfen kapsamındaki uygulamaları durdurmaya ve yeniden başlatmaya zorlayın</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed modülü güncellendi, sistemin yeniden başlatılması gerekiyor</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s Güncelleme kapsamı Sistem Çerçevesi içerdiğinden, değişiklikleri uygulamak için yeniden başlatma gereklidir</string>\n    <string name=\"module_updated_channel_name\">Modül güncellemesi tamamlandı</string>\n    <string name=\"status_channel_name\">LSPosed durumu</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed yüklendi</string>\n    <string name=\"lsposed_running_notification_content\">Yöneticiyi açmak için bildirime dokunun</string>\n    <string name=\"xposed_module_request_scope_title\">Kapsam Talebi</string>\n    <string name=\"xposed_module_request_scope_content\">Kullanıcı %2$s %1$s kapsamına %3$s eklemek ister.</string>\n    <string name=\"scope_channel_name\">Kapsam talebi</string>\n    <string name=\"scope_approve\">Onayla</string>\n    <string name=\"scope_deny\">Reddet</string>\n    <string name=\"nerver_ask_again\">Asla Sorma</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-uk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Модуль Xposed ще не активований</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s було встановлено, але ще не активовано</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s було встановлено до користувача %2$s, але ще не активовано</string>\n    <string name=\"xposed_module_updated_notification_title\">Модуль Xposed оновлено</string>\n    <string name=\"xposed_module_updated_notification_content\">%s було оновлено, будь ласка, примусово перезапустіть програми з області модуля</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Модуль Xposed оновлено, потрібно перезавантаження системи</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s було оновлено, оскільки область містить System Framework, необхідне перезавантаження для застосування змін</string>\n    <string name=\"module_updated_channel_name\">Оновлення модуля завершено</string>\n    <string name=\"status_channel_name\">Статус LSPosed</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed завантажено</string>\n    <string name=\"lsposed_running_notification_content\">Натисніть на повідомлення, щоб відкрити менеджер</string>\n    <string name=\"xposed_module_request_scope_title\">Запит на визначення обсягу робіт</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s на запит користувача %2$s з проханням додати %3$s до своєї області видимості.</string>\n    <string name=\"scope_channel_name\">Запит обсягу робіт</string>\n    <string name=\"scope_approve\">Затвердити</string>\n    <string name=\"scope_deny\">Відхилити</string>\n    <string name=\"nerver_ask_again\">Ніколи не питай</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-ur/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed ماڈیول ابھی تک چالو نہیں ہوا ہے۔</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s انسٹال ہو چکا ہے، لیکن ابھی تک چالو نہیں ہوا ہے۔</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s کو صارف %2$sپر انسٹال کر دیا گیا ہے، لیکن ابھی تک فعال نہیں ہوا ہے۔</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed ماڈیول کو اپ ڈیٹ کر دیا گیا۔</string>\n    <string name=\"xposed_module_updated_notification_content\">%s کو اپ ڈیٹ کر دیا گیا ہے، براہ کرم اس کے دائرہ کار میں ایپس کو زبردستی روکنے اور دوبارہ شروع کریں۔</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed ماڈیول کو اپ ڈیٹ کر دیا گیا، سسٹم ریبوٹ درکار ہے۔</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s کو اپ ڈیٹ کر دیا گیا ہے، چونکہ دائرہ کار میں سسٹم فریم ورک ہے، تبدیلیاں لاگو کرنے کے لیے ریبوٹ کی ضرورت ہے۔</string>\n    <string name=\"module_updated_channel_name\">ماڈیول اپ ڈیٹ مکمل ہو گیا۔</string>\n    <string name=\"status_channel_name\">ایل ایس پیز کی حیثیت</string>\n    <string name=\"lsposed_running_notification_title\">ایل ایس پیز لوڈ شدہ</string>\n    <string name=\"lsposed_running_notification_content\">مینیجر کو کھولنے کے لیے نوٹیفکیشن کو تھپتھپائیں۔</string>\n    <string name=\"xposed_module_request_scope_title\">دائرہ کار کی درخواست</string>\n    <string name=\"xposed_module_request_scope_content\">صارف %2$s پر %1$s اپنے دائرہ کار میں %3$s شامل کرنے کی درخواست کرتا ہے۔</string>\n    <string name=\"scope_channel_name\">دائرہ کار کی درخواست</string>\n    <string name=\"scope_approve\">منظور کرو</string>\n    <string name=\"scope_deny\">انکار کرنا</string>\n    <string name=\"nerver_ask_again\">کبھی نہ پوچھیں۔</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Mô-đun Xposed chưa được kích hoạt</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s đã được cài đặt, nhưng chưa được kích hoạt</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s vừa được cài đặt cho người dùng %2$s, nhưng chưa được kích hoạt</string>\n    <string name=\"xposed_module_updated_notification_title\">Mô-đun Xposed đã được cập nhật</string>\n    <string name=\"xposed_module_updated_notification_content\">%s đã được cập nhật, xin hãy buộc dừng và khởi động lại ứng dụng liên quan</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Mô-đun Xposed đã được cập nhật, yêu cầu khởi động lại hệ thống</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s đã được cập nhật, vì phạm vi bao gồm Framework Hệ thống, thì khởi động lại là cần thiết để áp dụng các thay đổi</string>\n    <string name=\"module_updated_channel_name\">Tiện ích bổ sung cập nhật hoàn tất</string>\n    <string name=\"status_channel_name\">Trạng thái hoạt động</string>\n    <string name=\"lsposed_running_notification_title\">Ứng dụng đã được tải</string>\n    <string name=\"lsposed_running_notification_content\">Nhấn để mở trình quản lý</string>\n    <string name=\"xposed_module_request_scope_title\">Yêu cầu phạm vi</string>\n    <string name=\"xposed_module_request_scope_content\">%1$s khi người dùng %2$s yêu cầu thêm %3$s vào phạm vi của nó.</string>\n    <string name=\"scope_channel_name\">Phạm vi yêu cầu</string>\n    <string name=\"scope_approve\">Chấp thuận</string>\n    <string name=\"scope_deny\">Từ chối</string>\n    <string name=\"nerver_ask_again\">Không hỏi lại</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed 模块尚未激活</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s 已安装，但尚未激活</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s 已安装到用户 %2$s，但尚未激活</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed 模块已更新</string>\n    <string name=\"xposed_module_updated_notification_content\">%s 已更新，请强行停止并重新打开其作用域内的应用</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed 模块已更新，需要重新启动</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s 已更新，由于作用域包含系统框架，需重启以应用更改</string>\n    <string name=\"module_updated_channel_name\">模块更新完成</string>\n    <string name=\"status_channel_name\">LSPosed 状态</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed 已加载</string>\n    <string name=\"lsposed_running_notification_content\">点按通知以打开管理器</string>\n    <string name=\"xposed_module_request_scope_title\">作用域请求</string>\n    <string name=\"xposed_module_request_scope_content\">用户 %2$s 上的 %1$s 请求将 %3$s 添加至其作用域。</string>\n    <string name=\"scope_channel_name\">作用域请求</string>\n    <string name=\"scope_approve\">允许</string>\n    <string name=\"scope_deny\">拒绝</string>\n    <string name=\"nerver_ask_again\">不再询问</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-zh-rHK/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed 模組尚未啟用</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s 已安裝，但尚未啟用</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s 已安裝到用戶 %2$s，但尚未啟用</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed 模組已更新</string>\n    <string name=\"xposed_module_updated_notification_content\">%s 已更新，請強制停止並重新開啟其作用範圍內的應用程式</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed 模組已更新，需要重新啟動。</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s 已更新，由於作用範圍包含系統架構，需要重新啟動以套用修改。</string>\n    <string name=\"module_updated_channel_name\">模块更新完成</string>\n    <string name=\"status_channel_name\">LSPosed 狀態</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed 已載入</string>\n    <string name=\"lsposed_running_notification_content\">輕觸通知以開啟管理員</string>\n    <string name=\"xposed_module_request_scope_title\">作用範圍要求</string>\n    <string name=\"xposed_module_request_scope_content\">用戶 %2$s 上的 %1$s 要求將 %3$s 新增至其作用範圍。</string>\n    <string name=\"scope_channel_name\">作用範圍要求</string>\n    <string name=\"scope_approve\">核准</string>\n    <string name=\"scope_deny\">拒絕</string>\n    <string name=\"nerver_ask_again\">永不詢問</string>\n</resources>\n"
  },
  {
    "path": "daemon/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Notification -->\n    <string name=\"module_is_not_activated_yet\">Xposed 模組尚未啟用</string>\n    <string name=\"module_is_not_activated_yet_main_user_detailed\">%1$s 已安裝，但尚未啟用</string>\n    <string name=\"module_is_not_activated_yet_multi_user_detailed\">%1$s 已安裝到使用者 %2$s，但尚未啟用</string>\n    <string name=\"xposed_module_updated_notification_title\">Xposed 模組已更新</string>\n    <string name=\"xposed_module_updated_notification_content\">%s 已更新，請強制停止並重新打開其作用域內的程式</string>\n    <string name=\"xposed_module_updated_notification_title_system\">Xposed 模組已更新，需要重新啟動。</string>\n    <string name=\"xposed_module_updated_notification_content_system\">%s 已更新，由於作用域包含系統框架，需要重新啟動以套用修改。</string>\n    <string name=\"module_updated_channel_name\">模組更新完成</string>\n    <string name=\"status_channel_name\">LSPosed 狀態</string>\n    <string name=\"lsposed_running_notification_title\">LSPosed 已載入</string>\n    <string name=\"lsposed_running_notification_content\">輕觸通知以開啟管理員</string>\n    <string name=\"xposed_module_request_scope_title\">作用範圍要求</string>\n    <string name=\"xposed_module_request_scope_content\">使用者 %2$s 上的 %1$s 要求將 %3$s 新增至其作用範圍。</string>\n    <string name=\"scope_channel_name\">作用範圍要求</string>\n    <string name=\"scope_approve\">核准</string>\n    <string name=\"scope_deny\">拒絕</string>\n    <string name=\"nerver_ask_again\">永不詢問</string>\n</resources>\n"
  },
  {
    "path": "dex2oat/.gitignore",
    "content": "/build\n/.cxx\n"
  },
  {
    "path": "dex2oat/README.md",
    "content": "# VectorDex2Oat\n\nVectorDex2Oat is a specialized wrapper and instrumentation suite for the Android `dex2oat` (Ahead-of-Time compiler) binary. It is designed to intercept the compilation process, force specific compiler behaviors (specifically disabling method inlining), and transparently spoof the resulting OAT metadata to hide the presence of the wrapper.\n\n## Overview\n\nIn the Android Runtime (ART), `dex2oat` compiles DEX files into OAT files. Modern ART optimizations often inline methods, making it difficult for instrumentation tools to hook specific function calls. \n\nThis project consists of two primary components:\n1.  **dex2oat (Wrapper):** A replacement binary that intercepts the execution, communicates via Unix Domain Sockets to obtain the original compiler binary, and executes it with forced flags.\n2.  **liboat_hook.so (Hooker):** A shared library injected into the `dex2oat` process via `LD_PRELOAD` that utilizes PLT hooking to sanitize the OAT header's command-line metadata.\n\n## Key Features\n\n*   **Inlining Suppression:** Appends `--inline-max-code-units=0` to the compiler arguments, ensuring all methods remain discrete and hookable.\n*   **FD-Based Execution:** Executes the original `dex2oat` via the system linker using `/proc/self/fd/` paths, avoiding direct execution of files on the disk.\n*   **Metadata Spoofing:** Intercepts `art::OatHeader::ComputeChecksum` or `art::OatHeader::GetKeyValueStore` to remove traces of the wrapper and its injected flags from the final `.oat` file.\n*   **Abstract Socket Communication:** Uses the Linux Abstract Namespace for Unix sockets to coordinate file descriptor passing between the controller and the wrapper.\n\n## Architecture\n\n### The Wrapper [dex2oat.cpp](src/main/cpp/dex2oat.cpp)\nThe wrapper acts as a \"man-in-the-middle\" for the compiler. When called by the system, it\n1.  connects to a predefined Unix socket (the stub name `5291374ceda0...` will be replaced during installation of `Vector`);\n2.  identifies the target architecture (32-bit vs 64-bit) and debug status;\n3.  receives File Descriptors (FDs) for both the original `dex2oat` binary and the `oat_hook` library;\n4.  reconstructs the command line, replacing the wrapper path with the original binary path and appending the \"no-inline\" flags;\n5.  clears `LD_LIBRARY_PATH` and sets `LD_PRELOAD` to the hooker library's FD;\n6.  invokes the dynamic linker (`linker64`) to execute the compiler.\n\n### The Hooker [oat_hook.cpp](src/main/cpp/oat_hook.cpp)\nThe hooker library is preloaded into the compiler's address space. It uses the [LSPlt](https://github.com/JingMatrix/LSPlt) library to:\n1.  Scan the memory map to find the `dex2oat` binary.\n2.  Locate and hook internal ART functions:\n    *   [art::OatHeader::GetKeyValueStore](https://cs.android.com/android/platform/superproject/+/android-latest-release:art/runtime/oat/oat.cc;l=366)\n    *   [art::OatHeader::ComputeChecksum](https://cs.android.com/android/platform/superproject/+/android-latest-release:art/runtime/oat/oat.cc;l=366)\n3.  When the compiler attempts to write the \"dex2oat-cmdline\" key into the OAT header, the hooker intercepts the call, parses the key-value store, and removes the wrapper-specific flags and paths.\n"
  },
  {
    "path": "dex2oat/build.gradle.kts",
    "content": "plugins { alias(libs.plugins.agp.lib) }\n\nandroid {\n    namespace = \"org.matrix.vector.dex2oat\"\n\n    androidResources { enable = false }\n\n    externalNativeBuild { cmake { path(\"src/main/cpp/CMakeLists.txt\") } }\n}\n"
  },
  {
    "path": "dex2oat/src/main/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(dex2oat)\n\nadd_executable(dex2oat dex2oat.cpp)\nadd_library(oat_hook SHARED oat_hook.cpp)\n\nOPTION(LSPLT_BUILD_SHARED OFF)\nadd_subdirectory(${VECTOR_ROOT}/external/lsplt/lsplt/src/main/jni external)\n\ntarget_include_directories(oat_hook PUBLIC include)\ntarget_include_directories(dex2oat PUBLIC include)\ntarget_link_libraries(dex2oat log)\ntarget_link_libraries(oat_hook log lsplt_static)\n\nif (DEFINED DEBUG_SYMBOLS_PATH)\n    message(STATUS \"Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}\")\n    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD\n            COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}\n            COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:${PROJECT_NAME}>\n            ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug\n            COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>\n            COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug\n            $<TARGET_FILE:${PROJECT_NAME}>)\nendif()\n"
  },
  {
    "path": "dex2oat/src/main/cpp/dex2oat.cpp",
    "content": "#include <linux/memfd.h>\n#include <sys/sendfile.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <string>\n#include <vector>\n\n#include \"logging.h\"\n\n// Access to the process environment variables\nextern \"C\" char **environ;\n\n#if defined(__LP64__)\n#define LP_SELECT(lp32, lp64) lp64\n#else\n#define LP_SELECT(lp32, lp64) lp32\n#endif\n\nnamespace {\n\nconstexpr char kSockName[] = \"5291374ceda0aef7c5d86cd2a4f6a3ac\";\n\n/**\n * Calculates a vector ID based on architecture and debug status.\n */\ninline int get_id_vec(bool is64, bool is_debug) {\n    return (static_cast<int>(is64) << 1) | static_cast<int>(is_debug);\n}\n\n/**\n * Wraps recvmsg with error logging.\n */\nssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {\n    ssize_t rec = recvmsg(sockfd, msg, flags);\n    if (rec < 0) {\n        PLOGE(\"recvmsg\");\n    }\n    return rec;\n}\n\n/**\n * Receives file descriptors passed over a Unix domain socket using SCM_RIGHTS.\n *\n * @return Pointer to the FD data on success, nullptr on failure.\n */\nvoid *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) {\n    struct iovec iov = {\n        .iov_base = &cnt,\n        .iov_len = sizeof(cnt),\n    };\n    struct msghdr msg = {.msg_name = nullptr,\n                         .msg_namelen = 0,\n                         .msg_iov = &iov,\n                         .msg_iovlen = 1,\n                         .msg_control = cmsgbuf,\n                         .msg_controllen = bufsz,\n                         .msg_flags = 0};\n\n    if (xrecvmsg(sockfd, &msg, MSG_WAITALL) < 0) return nullptr;\n\n    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);\n\n    if (msg.msg_controllen != bufsz || cmsg == nullptr ||\n        cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || cmsg->cmsg_level != SOL_SOCKET ||\n        cmsg->cmsg_type != SCM_RIGHTS) {\n        return nullptr;\n    }\n\n    return CMSG_DATA(cmsg);\n}\n\n/**\n * Helper to receive a single FD from the socket.\n */\nint recv_fd(int sockfd) {\n    char cmsgbuf[CMSG_SPACE(sizeof(int))];\n    void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1);\n    if (data == nullptr) return -1;\n\n    int result;\n    std::memcpy(&result, data, sizeof(int));\n    return result;\n}\n\n/**\n * Reads an integer acknowledgment from the socket.\n */\nint read_int(int fd) {\n    int val;\n    if (read(fd, &val, sizeof(val)) != sizeof(val)) return -1;\n    return val;\n}\n\n/**\n * Writes an integer command/ID to the socket.\n */\nvoid write_int(int fd, int val) {\n    if (fd < 0) return;\n    (void)write(fd, &val, sizeof(val));\n}\n\n}  // namespace\n\nint main(int argc, char **argv) {\n    LOGD(\"dex2oat wrapper ppid=%d\", getppid());\n\n    // Prepare Unix domain socket address (Abstract Namespace)\n    struct sockaddr_un sock = {};\n    sock.sun_family = AF_UNIX;\n    // sock.sun_path[0] is already \\0, so we copy name into sun_path + 1\n    std::strncpy(sock.sun_path + 1, kSockName, sizeof(sock.sun_path) - 2);\n\n    // Abstract socket length: family + leading \\0 + string length\n    socklen_t len = sizeof(sock.sun_family) + strlen(kSockName) + 1;\n\n    // 1. Get original dex2oat binary FD\n    int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);\n    if (connect(sock_fd, reinterpret_cast<struct sockaddr *>(&sock), len)) {\n        PLOGE(\"failed to connect to %s\", sock.sun_path + 1);\n        return 1;\n    }\n\n    bool is_debug = (argv[0] != nullptr && std::strstr(argv[0], \"dex2oatd\") != nullptr);\n    write_int(sock_fd, get_id_vec(LP_SELECT(false, true), is_debug));\n\n    int stock_fd = recv_fd(sock_fd);\n    read_int(sock_fd);  // Sync\n    close(sock_fd);\n\n    // 2. Get liboat_hook.so FD\n    sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);\n    if (connect(sock_fd, reinterpret_cast<struct sockaddr *>(&sock), len)) {\n        PLOGE(\"failed to connect to %s\", sock.sun_path + 1);\n        return 1;\n    }\n\n    write_int(sock_fd, LP_SELECT(4, 5));\n    int hooker_fd = recv_fd(sock_fd);\n    read_int(sock_fd);  // Sync\n    close(sock_fd);\n\n    if (hooker_fd == -1) {\n        LOGE(\"failed to read liboat_hook.so\");\n    } else {\n        int mem_fd = syscall(__NR_memfd_create, \"liboat_hook_memfd\", 0);\n        if (mem_fd >= 0) {\n            // Get the exact size of the original library\n            LOGD(\"Copying %d as mem_fd %d\", hooker_fd, mem_fd);\n            struct stat st;\n            if (fstat(hooker_fd, &st) == 0) {\n                // Tell the kernel to copy the entire file directly to the memfd\n                off_t offset = 0;\n                sendfile(mem_fd, hooker_fd, &offset, st.st_size);\n\n                // Swap the old FD with the new memfd\n                close(hooker_fd);\n                hooker_fd = mem_fd;\n            } else {\n                PLOGE(\"fstat failed\");\n                close(mem_fd);\n            }\n        } else {\n            PLOGE(\"memfd_create failed, falling back to original fd\");\n        }\n    }\n\n    LOGD(\"sock: %s stock_fd: %d\", sock.sun_path + 1, stock_fd);\n\n    // Prepare arguments for execve\n    // Logic: [linker] [/proc/self/fd/stock_fd] [original_args...] [--inline-max-code-units=0]\n    std::vector<const char *> exec_argv;\n\n    const char *linker_path =\n        LP_SELECT(\"/apex/com.android.runtime/bin/linker\", \"/apex/com.android.runtime/bin/linker64\");\n\n    char stock_fd_path[64];\n    std::snprintf(stock_fd_path, sizeof(stock_fd_path), \"/proc/self/fd/%d\", stock_fd);\n\n    exec_argv.push_back(linker_path);\n    exec_argv.push_back(stock_fd_path);\n\n    // Append original arguments starting from argv[1]\n    for (int i = 1; i < argc; ++i) {\n        exec_argv.push_back(argv[i]);\n    }\n\n    // Append hooking flags to disable inline, which is our purpose of this wrapper, since we cannot\n    // hook inlined target methods.\n    exec_argv.push_back(\"--inline-max-code-units=0\");\n    exec_argv.push_back(nullptr);\n\n    // Setup Environment variables\n    // Clear LD_LIBRARY_PATH to let the linker use internal config\n    unsetenv(\"LD_LIBRARY_PATH\");\n\n    // Set LD_PRELOAD to point to the hooker library FD\n    std::string preload_val = \"LD_PRELOAD=/proc/self/fd/\" + std::to_string(hooker_fd);\n    LOGD(\"Inject oat hook via %s\", preload_val.data());\n    setenv(\"LD_PRELOAD\", (\"/proc/self/fd/\" + std::to_string(hooker_fd)).c_str(), 1);\n\n    // Pass original argv[0] as DEX2OAT_CMD\n    if (argv[0]) {\n        setenv(\"DEX2OAT_CMD\", argv[0], 1);\n        LOGD(\"DEX2OAT_CMD set to %s\", argv[0]);\n    }\n\n    LOGI(\"Executing via linker: %s executing %s\", linker_path, stock_fd_path);\n\n    // Perform the execution\n    execve(linker_path, const_cast<char *const *>(exec_argv.data()), environ);\n\n    // If we reach here, execve failed\n    PLOGE(\"execve failed\");\n    return 2;\n}\n"
  },
  {
    "path": "dex2oat/src/main/cpp/include/base_macros.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS 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#pragma once\n\n#include <stddef.h>  // for size_t\n#include <unistd.h>  // for TEMP_FAILURE_RETRY\n\n#include <utility>\n\n// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.\n#ifndef TEMP_FAILURE_RETRY\n#define TEMP_FAILURE_RETRY(exp)                                                                    \\\n    ({                                                                                             \\\n        decltype(exp) _rc;                                                                         \\\n        do {                                                                                       \\\n            _rc = (exp);                                                                           \\\n        } while (_rc == -1 && errno == EINTR);                                                     \\\n        _rc;                                                                                       \\\n    })\n#endif\n\n// A macro to disallow the copy constructor and operator= functions\n// This must be placed in the private: declarations for a class.\n//\n// For disallowing only assign or copy, delete the relevant operator or\n// constructor, for example:\n// void operator=(const TypeName&) = delete;\n// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken\n// semantically, one should either use disallow both or neither. Try to\n// avoid these in new code.\n#define DISALLOW_COPY_AND_ASSIGN(TypeName)                                                         \\\n    TypeName(const TypeName &) = delete;                                                           \\\n    void operator=(const TypeName &) = delete\n\n// A macro to disallow all the implicit constructors, namely the\n// default constructor, copy constructor and operator= functions.\n//\n// This should be used in the private: declarations for a class\n// that wants to prevent anyone from instantiating it. This is\n// especially useful for classes containing only static methods.\n#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName)                                                   \\\n    TypeName() = delete;                                                                           \\\n    DISALLOW_COPY_AND_ASSIGN(TypeName)\n\n// The arraysize(arr) macro returns the # of elements in an array arr.\n// The expression is a compile-time constant, and therefore can be\n// used in defining new arrays, for example.  If you use arraysize on\n// a pointer by mistake, you will get a compile-time error.\n//\n// One caveat is that arraysize() doesn't accept any array of an\n// anonymous type or a type defined inside a function.  In these rare\n// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below.  This is\n// due to a limitation in C++'s template system.  The limitation might\n// eventually be removed, but it hasn't happened yet.\n\n// This template function declaration is used in defining arraysize.\n// Note that the function doesn't need an implementation, as we only\n// use its type.\ntemplate <typename T, size_t N>\nchar (&ArraySizeHelper(T (&array)[N]))[N];  // NOLINT(readability/casting)\n\n#define arraysize(array) (sizeof(ArraySizeHelper(array)))\n\n#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)\n\n// Changing this definition will cause you a lot of pain.  A majority of\n// vendor code defines LIKELY and UNLIKELY this way, and includes\n// this header through an indirect path.\n#define LIKELY(exp) (__builtin_expect((exp) != 0, true))\n#define UNLIKELY(exp) (__builtin_expect((exp) != 0, false))\n\n#define WARN_UNUSED __attribute__((warn_unused_result))\n\n// A deprecated function to call to create a false use of the parameter, for\n// example:\n//   int foo(int x) { UNUSED(x); return 10; }\n// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.\ntemplate <typename... T>\nvoid UNUSED(const T &...) {}\n\n// An attribute to place on a parameter to a function, for example:\n//   int foo(int x ATTRIBUTE_UNUSED) { return 10; }\n// to avoid compiler warnings.\n#define ATTRIBUTE_UNUSED __attribute__((__unused__))\n\n// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through\n// between switch labels:\n//  switch (x) {\n//    case 40:\n//    case 41:\n//      if (truth_is_out_there) {\n//        ++x;\n//        FALLTHROUGH_INTENDED;  // Use instead of/along with annotations in\n//                               // comments.\n//      } else {\n//        return x;\n//      }\n//    case 42:\n//      ...\n//\n// As shown in the example above, the FALLTHROUGH_INTENDED macro should be\n// followed by a semicolon. It is designed to mimic control-flow statements\n// like 'break;', so it can be placed in most places where 'break;' can, but\n// only if there are no statements on the execution path between it and the\n// next switch label.\n//\n// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to\n// [[clang::fallthrough]] attribute, which is analysed when performing switch\n// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang\n// documentation on language extensions for details:\n// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough\n//\n// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no\n// effect on diagnostics.\n//\n// In either case this macro has no effect on runtime behavior and performance\n// of code.\n#ifndef FALLTHROUGH_INTENDED\n#define FALLTHROUGH_INTENDED [[fallthrough]]  // NOLINT\n#endif\n\n// Current ABI string\n#if defined(__arm__)\n#define ABI_STRING \"arm\"\n#elif defined(__aarch64__)\n#define ABI_STRING \"arm64\"\n#elif defined(__i386__)\n#define ABI_STRING \"x86\"\n#elif defined(__riscv)\n#define ABI_STRING \"riscv64\"\n#elif defined(__x86_64__)\n#define ABI_STRING \"x86_64\"\n#endif\n"
  },
  {
    "path": "dex2oat/src/main/cpp/include/logging.h",
    "content": "#pragma once\n\n#include <android/log.h>\n#include <errno.h>\n\n#ifndef LOG_TAG\n#define LOG_TAG \"VectorDex2Oat\"\n#endif\n\n#ifdef LOG_DISABLED\n#define LOGD(...) 0\n#define LOGV(...) 0\n#define LOGI(...) 0\n#define LOGW(...) 0\n#define LOGE(...) 0\n#else\n#ifndef NDEBUG\n#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)\n#define LOGV(fmt, ...)                                                                             \\\n    __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,                                              \\\n                        \"%s:%d#%s\"                                                                 \\\n                        \": \" fmt,                                                                  \\\n                        __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)\n#else\n#define LOGD(...) 0\n#define LOGV(...) 0\n#endif\n#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)\n#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)\n#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)\n#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)\n#define PLOGE(fmt, args...) LOGE(fmt \" failed with %d: %s\", ##args, errno, strerror(errno))\n#endif\n"
  },
  {
    "path": "dex2oat/src/main/cpp/include/macros.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS 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#ifndef ART_LIBARTBASE_BASE_MACROS_H_\n#define ART_LIBARTBASE_BASE_MACROS_H_\n\n#include <stddef.h>  // for size_t\n#include <unistd.h>  // for TEMP_FAILURE_RETRY\n\n// Declare a friend relationship in a class with a test. Used rather that FRIEND_TEST to avoid\n// globally importing gtest/gtest.h into the main ART header files.\n#define ART_FRIEND_TEST(test_set_name, individual_test)                                            \\\n    friend class test_set_name##_##individual_test##_Test\n\n// Declare a friend relationship in a class with a typed test.\n#define ART_FRIEND_TYPED_TEST(test_set_name, individual_test)                                      \\\n    template <typename T>                                                                          \\\n    ART_FRIEND_TEST(test_set_name, individual_test)\n\n// Shorthand for formatting with compile time checking of the format string\n#define ART_FORMAT(str, ...) ::fmt::format(FMT_STRING(str), __VA_ARGS__)\n\n// A macro to disallow new and delete operators for a class. It goes in the private: declarations.\n// NOTE: Providing placement new (and matching delete) for constructing container elements.\n#define DISALLOW_ALLOCATION()                                                                      \\\npublic:                                                                                            \\\n    NO_RETURN ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); }                 \\\n    ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; }                   \\\n    ALWAYS_INLINE void operator delete(void*, void*) noexcept {}                                   \\\n                                                                                                   \\\nprivate:                                                                                           \\\n    void* operator new(size_t) = delete  // NOLINT\n\n// offsetof is not defined by the spec on types with non-standard layout,\n// however it is implemented by compilers in practice.\n// (note that reinterpret_cast is not valid constexpr)\n//\n// Alternative approach would be something like:\n// #define OFFSETOF_HELPER(t, f) \\\n//   (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u))\n// #define OFFSETOF_MEMBER(t, f) \\\n//   (__builtin_constant_p(OFFSETOF_HELPER(t,f)) ? OFFSETOF_HELPER(t,f) : OFFSETOF_HELPER(t,f))\n#define OFFSETOF_MEMBER(t, f) offsetof(t, f)\n\n#define OFFSETOF_MEMBERPTR(t, f)                                                                   \\\n    (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) -                                \\\n     static_cast<uintptr_t>(16))  // NOLINT\n\n#define ALIGNED(x) __attribute__((__aligned__(x)))\n#define PACKED(x) __attribute__((__aligned__(x), __packed__))\n\n// Stringify the argument.\n#define QUOTE(x) #x\n#define STRINGIFY(x) QUOTE(x)\n\n// Append tokens after evaluating.\n#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a##b\n#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b)\n\n#ifndef NDEBUG\n#define ALWAYS_INLINE\n#define FLATTEN\n#else\n#define ALWAYS_INLINE __attribute__((always_inline))\n#define FLATTEN __attribute__((flatten))\n#endif\n\n#define NO_STACK_PROTECTOR __attribute__((no_stack_protector))\n\n// clang doesn't like attributes on lambda functions. It would be nice to say:\n//   #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE\n#define ALWAYS_INLINE_LAMBDA\n\n#define NO_INLINE __attribute__((noinline))\n\n#if defined(__APPLE__)\n#define HOT_ATTR\n#define COLD_ATTR\n#else\n#define HOT_ATTR __attribute__((hot))\n#define COLD_ATTR __attribute__((cold))\n#endif\n\n#define PURE __attribute__((__pure__))\n\n// Define that a position within code is unreachable, for example:\n//   int foo () { LOG(FATAL) << \"Don't call me\"; UNREACHABLE(); }\n// without the UNREACHABLE a return statement would be necessary.\n#define UNREACHABLE __builtin_unreachable\n\n// Add the C++11 noreturn attribute.\n#define NO_RETURN [[noreturn]]  // NOLINT[whitespace/braces] [5]\n\n// Annotalysis thread-safety analysis support. Things that are not in base.\n\n#define LOCKABLE CAPABILITY(\"mutex\")\n#define SHARED_LOCKABLE SHARED_CAPABILITY(\"mutex\")\n\n// Some of the libs (e.g. libarttest(d)) require more public symbols when built\n// in debug configuration.\n// Using symbol visibility only for release builds allows to reduce the list of\n// exported symbols and eliminates the need to check debug build configurations\n// when changing the exported symbols.\n#ifdef NDEBUG\n#define HIDDEN __attribute__((visibility(\"hidden\")))\n#define PROTECTED __attribute__((visibility(\"protected\")))\n#define EXPORT __attribute__((visibility(\"default\")))\n#else\n#define HIDDEN\n#define PROTECTED\n#define EXPORT\n#endif\n\n// Protected symbols must be declared with \"protected\" visibility attribute when\n// building the library and \"default\" visibility when referred to from external\n// libraries/binaries. Otherwise, the external code will expect the symbol to be\n// defined locally and fail to link.\n#ifdef BUILDING_LIBART\n#define LIBART_PROTECTED PROTECTED\n#else\n#define LIBART_PROTECTED EXPORT\n#endif\n\n// Some global variables shouldn't be visible outside libraries declaring them.\n// The attribute allows hiding them, so preventing direct access.\n#define ALWAYS_HIDDEN __attribute__((visibility(\"hidden\")))\n\n#endif  // ART_LIBARTBASE_BASE_MACROS_H_\n"
  },
  {
    "path": "dex2oat/src/main/cpp/include/oat.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS 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#ifndef ART_RUNTIME_OAT_OAT_H_\n#define ART_RUNTIME_OAT_OAT_H_\n\n#include <array>\n#include <cstddef>\n#include <string_view>\n#include <utility>\n\n#include \"base_macros.h\"\n#include \"macros.h\"\n\nnamespace art {\n\nenum class InstructionSet;\n\nclass EXPORT PACKED(4) OatHeader {\npublic:\n    static constexpr std::array<uint8_t, 4> kOatMagic{{'o', 'a', 't', '\\n'}};\n    // Last oat version changed reason: Ensure oat checksum determinism across hosts and devices.\n    static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '5', '9', '\\0'}};\n\n    static constexpr const char* kDex2OatCmdLineKey = \"dex2oat-cmdline\";\n    static constexpr const char* kDebuggableKey = \"debuggable\";\n    static constexpr const char* kNativeDebuggableKey = \"native-debuggable\";\n    static constexpr const char* kCompilerFilter = \"compiler-filter\";\n    static constexpr const char* kClassPathKey = \"classpath\";\n    static constexpr const char* kBootClassPathKey = \"bootclasspath\";\n    static constexpr const char* kBootClassPathChecksumsKey = \"bootclasspath-checksums\";\n    static constexpr const char* kApexVersionsKey = \"apex-versions\";\n    static constexpr const char* kConcurrentCopying = \"concurrent-copying\";\n    static constexpr const char* kCompilationReasonKey = \"compilation-reason\";\n    static constexpr const char* kRequiresImage = \"requires-image\";\n\n    // Fields listed here are key value store fields that are deterministic across hosts and\n    // devices, meaning they should have exactly the same value when the oat file is generated on\n    // different hosts and devices for the same app / boot image and for the same device model with\n    // the same compiler options. If you are adding a new field that doesn't hold this property, put\n    // it in `kNonDeterministicFieldsAndLengths` and assign a length limit.\n    //\n    // When writing the oat header, the non-deterministic fields are padded to their length limits\n    // and excluded from the oat checksum computation. This makes the oat checksum deterministic\n    // across hosts and devices, which is important for Cloud Compilation, where we generate an oat\n    // file on a host and use it on a device.\n    static constexpr std::array<std::string_view, 9> kDeterministicFields{\n        kDebuggableKey,     kNativeDebuggableKey,  kCompilerFilter,\n        kClassPathKey,      kBootClassPathKey,     kBootClassPathChecksumsKey,\n        kConcurrentCopying, kCompilationReasonKey, kRequiresImage,\n    };\n\n    static constexpr std::array<std::pair<std::string_view, size_t>, 2>\n        kNonDeterministicFieldsAndLengths{\n            std::make_pair(kDex2OatCmdLineKey, 2048),\n            std::make_pair(kApexVersionsKey, 1024),\n        };\n\n    static constexpr const char kTrueValue[] = \"true\";\n    static constexpr const char kFalseValue[] = \"false\";\n\n    // Added helper to access the key_value_store_ field, which could be fragile across\n    // different Android versions and compiler optimizations.\n    const uint8_t* getKeyValueStore() const { return key_value_store_; }\n\n    void ComputeChecksum(/*inout*/ uint32_t* checksum) const;\n\nprivate:\n    std::array<uint8_t, 4> magic_;\n    std::array<uint8_t, 4> version_;\n    uint32_t oat_checksum_;\n\n    InstructionSet instruction_set_;\n    uint32_t instruction_set_features_bitmap_;\n    uint32_t dex_file_count_;\n    uint32_t oat_dex_files_offset_;\n    uint32_t bcp_bss_info_offset_;\n    // Offset of the oat header (i.e. start of the oat data) in the ELF file.\n    // It is used to additional validation of the oat header as it is not\n    // page-aligned in the memory.\n    uint32_t base_oat_offset_;\n    uint32_t executable_offset_;\n    uint32_t jni_dlsym_lookup_trampoline_offset_;\n    uint32_t jni_dlsym_lookup_critical_trampoline_offset_;\n    uint32_t quick_generic_jni_trampoline_offset_;\n    uint32_t quick_imt_conflict_trampoline_offset_;\n    uint32_t quick_resolution_trampoline_offset_;\n    uint32_t quick_to_interpreter_bridge_offset_;\n    uint32_t nterp_trampoline_offset_;\n\n    uint32_t key_value_store_size_;\n    uint8_t key_value_store_[0];  // note variable width data at end\n\n    DISALLOW_COPY_AND_ASSIGN(OatHeader);\n};\n}  // namespace art\n#endif  // ART_RUNTIME_OAT_OAT_H_\n"
  },
  {
    "path": "dex2oat/src/main/cpp/oat_hook.cpp",
    "content": "#include <dlfcn.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <cstring>\n#include <lsplt.hpp>\n#include <map>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"logging.h\"\n#include \"oat.h\"\n\n/**\n * This library is injected into dex2oat to intercept the generation of OAT headers. Our wrapper\n * runs dex2oat via the linker with extra flags. Without this hook, the resulting OAT file would\n * record the transferred fd path of wrapper and the extra flags in its \"dex2oat-cmdline\" key, which\n * can be used to detect the wrapper.\n */\n\nnamespace {\nconst std::string_view kParamToRemove = \"--inline-max-code-units=0\";\nstd::string g_binary_path = getenv(\"DEX2OAT_CMD\");  // The original binary path\n}  // namespace\n\n/**\n * Sanitizes the command line string by:\n * 1. Replacing the first token (the linker/binary path) with the original dex2oat path.\n * 2. Removing the specific optimization flag we injected.\n */\nstd::string process_cmd(std::string_view sv, std::string_view new_cmd_path) {\n    std::vector<std::string> tokens;\n    std::string current;\n\n    // Simple split by space\n    for (char c : sv) {\n        if (c == ' ') {\n            if (!current.empty()) {\n                tokens.push_back(std::move(current));\n                current.clear();\n            }\n        } else {\n            current.push_back(c);\n        }\n    }\n    if (!current.empty()) tokens.push_back(std::move(current));\n\n    // 1. Replace the command path (argv[0])\n    if (!tokens.empty()) {\n        tokens[0] = std::string(new_cmd_path);\n    }\n\n    // 2. Remove the injected parameter if it exists\n    auto it = std::remove(tokens.begin(), tokens.end(), std::string(kParamToRemove));\n    tokens.erase(it, tokens.end());\n\n    // 3. Join tokens back into a single string\n    std::string result;\n    for (size_t i = 0; i < tokens.size(); ++i) {\n        result += tokens[i];\n        if (i != tokens.size() - 1) result += ' ';\n    }\n    return result;\n}\n\n/**\n * Re-serializes the Key-Value map back into the OAT header memory space.\n */\nuint8_t* WriteKeyValueStore(const std::map<std::string, std::string>& key_values, uint8_t* store) {\n    LOGD(\"Writing KeyValueStore back to memory\");\n    char* data_ptr = reinterpret_cast<char*>(store);\n\n    for (const auto& [key, value] : key_values) {\n        // Copy key + null terminator\n        std::memcpy(data_ptr, key.c_str(), key.length() + 1);\n        data_ptr += key.length() + 1;\n        // Copy value + null terminator\n        std::memcpy(data_ptr, value.c_str(), value.length() + 1);\n        data_ptr += value.length() + 1;\n    }\n    LOGD(\"Written KeyValueStore with size: %zu\", reinterpret_cast<uint8_t*>(data_ptr) - store);\n\n    return reinterpret_cast<uint8_t*>(data_ptr);\n}\n\n// Helper function to test if a header field could have variable length\nbool IsNonDeterministic(const std::string_view& key) {\n    auto variable_fields = art::OatHeader::kNonDeterministicFieldsAndLengths;\n    return std::any_of(variable_fields.begin(), variable_fields.end(),\n                       [&key](const auto& pair) { return pair.first.compare(key) == 0; });\n}\n\n/**\n * Parses the OAT KeyValueStore and spoofs the \"dex2oat-cmdline\" entry.\n *\n * @return true if the store was modified in-place or successfully rebuilt.\n */\nbool SpoofKeyValueStore(uint8_t* store) {\n    if (!store) return false;\n\n    uint32_t* const store_size_ptr = reinterpret_cast<uint32_t*>(store - sizeof(uint32_t));\n    uint32_t const store_size = *store_size_ptr;\n\n    const char* ptr = reinterpret_cast<const char*>(store);\n    const char* const store_end = ptr + store_size;\n    std::map<std::string, std::string> new_store_map;\n    LOGI(\"Parsing KeyValueStore [%p - %p] of size %u\", ptr, store_end, store_size);\n\n    bool store_modified = false;\n\n    while (ptr < store_end && *ptr != '\\0') {\n        // Find key\n        const char* key_end = reinterpret_cast<const char*>(std::memchr(ptr, 0, store_end - ptr));\n        if (!key_end) break;\n        std::string_view key(ptr, key_end - ptr);\n\n        // Find value\n        const char* value_start = key_end + 1;\n        if (value_start >= store_end) break;\n        const char* value_end =\n            reinterpret_cast<const char*>(std::memchr(value_start, 0, store_end - value_start));\n        if (!value_end) break;\n        std::string_view value(value_start, value_end - value_start);\n\n        const bool has_padding =\n            value_end + 1 < store_end && *(value_end + 1) == '\\0' && IsNonDeterministic(key);\n\n        if (key == art::OatHeader::kDex2OatCmdLineKey &&\n            value.find(kParamToRemove) != std::string_view::npos) {\n            std::string cleaned_cmd = process_cmd(value, g_binary_path);\n            LOGI(\"Spoofing cmdline: Original size %zu -> New size %zu\", value.length(),\n                 cleaned_cmd.length());\n\n            // We can overwrite in-place if the padding is enabled\n            if (has_padding) {\n                LOGI(\"In-place spoofing dex2oat-cmdline (padding detected)\");\n\n                // Zero out the entire original value range to be safe\n                size_t original_capacity = value.length();\n                std::memset(const_cast<char*>(value_start), 0, original_capacity);\n\n                // Write the new command.\n                std::memcpy(const_cast<char*>(value_start), cleaned_cmd.c_str(),\n                            std::min(cleaned_cmd.length(), original_capacity));\n                return true;\n            }\n\n            // Standard logic: store in map and rebuild later\n            new_store_map[std::string(key)] = std::move(cleaned_cmd);\n            store_modified = true;\n        } else {\n            new_store_map[std::string(key)] = std::string(value);\n            LOGI(\"Parsed item:\\t[%s:%s]\", key.data(), value.data());\n        }\n\n        ptr = value_end + 1;\n        if (has_padding) {\n            while (*ptr == '\\0') {\n                ptr++;\n            }\n        }\n    }\n\n    if (store_modified) {\n        uint8_t* const new_store_end = WriteKeyValueStore(new_store_map, store);\n        *store_size_ptr = new_store_end - store;\n        LOGI(\"Store size set to %u\", *store_size_ptr);\n        return true;\n    }\n    return false;\n}\n\n#define DCL_HOOK_FUNC(ret, func, ...)                                                              \\\n    ret (*old_##func)(__VA_ARGS__) = nullptr;                                                      \\\n    ret new_##func(__VA_ARGS__)\n\n// For Android version < 16\nDCL_HOOK_FUNC(uint8_t*, _ZNK3art9OatHeader16GetKeyValueStoreEv, void* header) {\n    uint8_t* const key_value_store = old__ZNK3art9OatHeader16GetKeyValueStoreEv(header);\n\n    SpoofKeyValueStore(key_value_store);\n\n    return key_value_store;\n}\n\n// For Android version 16+ : Intercept during checksum calculation\nDCL_HOOK_FUNC(void, _ZNK3art9OatHeader15ComputeChecksumEPj, void* header, uint32_t* checksum) {\n    auto* oat_header = reinterpret_cast<art::OatHeader*>(header);\n    uint8_t* const store = const_cast<uint8_t*>(oat_header->getKeyValueStore());\n\n    SpoofKeyValueStore(store);\n\n    // Call original to compute checksum on our modified data\n    old__ZNK3art9OatHeader15ComputeChecksumEPj(header, checksum);\n    LOGV(\"OAT Checksum recalculated: 0x%08X\", *checksum);\n}\n\n#undef DCL_HOOK_FUNC\n\nvoid register_hook(dev_t dev, ino_t inode, const char* symbol, void* new_func, void** old_func) {\n    if (!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) {\n        LOGE(\"Failed to register PLT hook: %s\", symbol);\n    }\n}\n\n#define PLT_HOOK_REGISTER_SYM(DEV, INODE, SYM, NAME)                                               \\\n    register_hook(DEV, INODE, SYM, reinterpret_cast<void*>(new_##NAME),                            \\\n                  reinterpret_cast<void**>(&old_##NAME))\n\n#define PLT_HOOK_REGISTER(DEV, INODE, NAME) PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME)\n\n__attribute__((constructor)) static void initialize() {\n    dev_t dev = 0;\n    ino_t inode = 0;\n\n    // Locate the dex2oat binary in memory to get its device and inode for PLT hooking\n    for (const auto& info : lsplt::MapInfo::Scan()) {\n        if (info.path.find(\"bin/dex2oat\") != std::string::npos) {\n            dev = info.dev;\n            inode = info.inode;\n            if (g_binary_path.empty()) g_binary_path = std::string(info.path);\n            LOGD(\"Found target: %s (dev: %ju, inode: %ju)\", info.path.data(), (uintmax_t)dev,\n                 (uintmax_t)inode);\n            break;\n        }\n    }\n\n    if (dev == 0) {\n        LOGE(\"Could not locate dex2oat memory map\");\n        return;\n    }\n\n    // Register hook for the standard KeyValueStore getter\n    PLT_HOOK_REGISTER(dev, inode, _ZNK3art9OatHeader16GetKeyValueStoreEv);\n\n    // If the standard store hook fails (e.g., on Android 16+), try the Checksum hook\n    if (!lsplt::CommitHook()) {\n        PLT_HOOK_REGISTER(dev, inode, _ZNK3art9OatHeader15ComputeChecksumEPj);\n        lsplt::CommitHook();\n    }\n}\n"
  },
  {
    "path": "external/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\nproject(external)\n\nset(CMAKE_CXX_STANDARD 23)\n\nset(XZ_SOURCES\n\txz_crc32.c\n\txz_crc64.c\n\t# xz_dec_bcj.c\n\txz_dec_lzma2.c\n\txz_dec_stream.c)\nset(XZ_INCLUDES xz-embedded/linux/include/linux xz-embedded/userspace)\nlist(TRANSFORM XZ_SOURCES PREPEND xz-embedded/linux/lib/xz/)\nadd_library(xz_static STATIC ${XZ_SOURCES})\ntarget_compile_options(xz_static PRIVATE -DXZ_USE_CRC64)\ntarget_include_directories(xz_static PRIVATE ${XZ_INCLUDES})\n\noption(LSPLANT_BUILD_SHARED OFF)\noption(Plugin.SymbolResolver OFF)\noption(FMT_INSTALL OFF)\n\nadd_subdirectory(dobby)\nadd_subdirectory(fmt)\nadd_subdirectory(lsplant/lsplant/src/main/jni)\ntarget_compile_options(lsplant_static PUBLIC -Wno-gnu-anonymous-struct)\ntarget_compile_definitions(fmt-header-only INTERFACE FMT_USE_LOCALE=0 FMT_USE_FLOAT=0 FMT_USE_DOUBLE=0 FMT_USE_LONG_DOUBLE=0 FMT_USE_BITINT=0)\n"
  },
  {
    "path": "external/README.md",
    "content": "# External Dependencies\n\nThis directory contains all the external dependencies required to build the Vector framework.\nThey are included as git submodules to ensure version consistency and timely updating.\n\n## Native dependencies\n\n-   [Dobby](https://github.com/JingMatrix/Dobby): \n    A lightweight, multi-platform inline hooking framework. It serves as the backend for all native function hooking (`HookInline`).\n\n-   [fmt](https://github.com/fmtlib/fmt):\n    A modern formatting library used for high-performance, type-safe logging throughout the native code.\n\n-   [LSPlant](https://github.com/JingMatrix/LSPlant):\n    A hooking framework for the Android Runtime (ART). It provides the core functionality for intercepting and modifying Java methods.\n\n-   [xz-embedded](https://github.com/tukaani-project/xz-embedded):\n    A lightweight data compression library with a small footprint. It is used by the ELF parser to decompress the `.gnu_debugdata` section of stripped native libraries.\n\n-   [LSPlt](https://github.com/JingMatrix/LSPlt):\n    A library for PLT (Procedure Linkage Table) hooking. It is used in the `dex2oat` sub-project to bypass a detection point. **Note:** This is included as a submodule for project convenience but is not compiled into the `external` C++ library itself.\n\n## Java libraries\n\n-   [apache/commons-lang](https://github.com/apache/commons-lang):\n    A package of Java utility classes for the classes that are in java.lang's hierarchy. Some classes are renamed and then used to implement the `XposedHelpers` API.\n\n-   [axml/manifest-editor](https://github.com/JingMatrix/ManifestEditor):\n    A a tool used to modify Android Manifest binary file. It is to parse manifestation files of Xposed modules.\n\n"
  },
  {
    "path": "external/apache/.gitignore",
    "content": "local/generated\n"
  },
  {
    "path": "external/apache/build.gradle.kts",
    "content": "val androidSourceCompatibility: JavaVersion by rootProject.extra\nval androidTargetCompatibility: JavaVersion by rootProject.extra\n\nplugins {\n    id(\"java-library\")\n}\n\njava {\n    sourceCompatibility = androidSourceCompatibility\n    targetCompatibility = androidTargetCompatibility\n    sourceSets {\n        main {\n            java.srcDirs(\"commons-lang/src/main/java\", \"local\")\n        }\n    }\n}\n\nval lang3Src = \"commons-lang/src/main/java/org/apache/commons/lang3\"\nval localDir = \"local/generated\"\n\ntasks.register<Copy>(\"ClassUtilsX\") {\n    from(\"$lang3Src/ClassUtils.java\")\n    into(localDir)\n    filter { line: String -> line.replace(\"ClassUtils\", \"ClassUtilsX\") }\n    rename(\"(.+).java\", \"$1X.java\")\n}\n\ntasks.register<Copy>(\"SerializationUtilsX\") {\n    from(\"$lang3Src/SerializationUtils.java\")\n    into(localDir)\n    filter { line: String -> line.replace(\"SerializationUtils\", \"SerializationUtilsX\") }\n    rename(\"(.+).java\", \"$1X.java\")\n}\n\ntasks.compileJava {\n    dependsOn(\"ClassUtilsX\")\n    dependsOn(\"SerializationUtilsX\")\n}\n"
  },
  {
    "path": "external/apache/local/MemberUtilsX.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.apache.commons.lang3.reflect;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\n\npublic class MemberUtilsX {\n    public static int compareConstructorFit(final Constructor<?> left, final Constructor<?> right, final Class<?>[] actual) {\n        return MemberUtils.compareConstructorFit(left, right, actual);\n    }\n\n    public static int compareMethodFit(final Method left, final Method right, final Class<?>[] actual) {\n        return MemberUtils.compareMethodFit(left, right, actual);\n    }\n}\n"
  },
  {
    "path": "external/axml/build.gradle.kts",
    "content": "val androidSourceCompatibility: JavaVersion by rootProject.extra\nval androidTargetCompatibility: JavaVersion by rootProject.extra\n\nplugins {\n    id(\"java-library\")\n}\n\njava {\n    sourceCompatibility = androidSourceCompatibility\n    targetCompatibility = androidTargetCompatibility\n    sourceSets {\n        main {\n            java.srcDirs(\"manifest-editor/lib/src/main/java\")\n            resources.srcDirs(\"manifest-editor/lib/src/main\")\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\nagp = \"8.13.1\"\nkotlin = \"2.3.10\"\nnav = \"2.9.7\"\nappcenter = \"5.0.5\"\nglide = \"5.0.5\"\nokhttp = \"5.3.2\"\nktfmt = \"0.25.0\"\n\n[plugins]\nagp-lib = { id = \"com.android.library\", version.ref = \"agp\" }\nagp-app = { id = \"com.android.application\", version.ref = \"agp\" }\nkotlin = { id = \"org.jetbrains.kotlin.android\", version.ref = \"kotlin\" }\nnav-safeargs = { id = \"androidx.navigation.safeargs\", version.ref = \"nav\" }\nautoresconfig = { id = \"dev.rikka.tools.autoresconfig\", version = \"1.2.2\" }\nktfmt = { id = \"com.ncorti.ktfmt.gradle\", version.ref = \"ktfmt\" }\nmaterialthemebuilder = { id = \"dev.rikka.tools.materialthemebuilder\", version = \"1.5.1\" }\nlsplugin-resopt = { id = \"org.lsposed.lsplugin.resopt\", version = \"1.6\" }\nlsplugin-apksign = { id = \"org.lsposed.lsplugin.apksign\", version = \"1.4\" }\n\n[libraries]\nrikkax-appcompat = { module = \"dev.rikka.rikkax.appcompat:appcompat\", version = \"1.6.1\" }\nrikkax-core = { module = \"dev.rikka.rikkax.core:core\", version = \"1.4.1\" }\nrikkax-insets = { module = \"dev.rikka.rikkax.insets:insets\", version = \"1.3.0\" }\nrikkax-layoutinflater = { module = \"dev.rikka.rikkax.layoutinflater:layoutinflater\", version = \"1.3.0\" }\nrikkax-material = { module = \"dev.rikka.rikkax.material:material\", version = \"2.7.2\" }\nrikkax-material-preference = { module = \"dev.rikka.rikkax.material:material-preference\", version = \"2.0.0\" }\nrikkax-parcelablelist = { module = \"dev.rikka.rikkax.parcelablelist:parcelablelist\", version = \"2.0.1\" }\nrikkax-recyclerview = { module = \"dev.rikka.rikkax.recyclerview:recyclerview-ktx\", version = \"1.3.2\" }\nrikkax-widget-borderview = { module = \"dev.rikka.rikkax.widget:borderview\", version = \"1.1.0\" }\nrikkax-widget-mainswitchbar = { module = \"dev.rikka.rikkax.widget:mainswitchbar\", version = \"1.0.2\" }\n\nandroidx-activity = { module = \"androidx.activity:activity\", version = \"1.12.4\" }\nandroidx-annotation = { module = \"androidx.annotation:annotation\", version = \"1.9.1\" }\nandroidx-browser = { module = \"androidx.browser:browser\", version = \"1.9.0\" }\nandroidx-constraintlayout = { module = \"androidx.constraintlayout:constraintlayout\", version = \"2.2.1\" }\nandroidx-core = { module = \"androidx.core:core\", version = \"1.17.0\" }\nandroidx-fragment = { module = \"androidx.fragment:fragment\", version = \"1.8.9\" }\nandroidx-navigation-fragment = { group = \"androidx.navigation\", name = \"navigation-fragment\", version.ref = \"nav\" }\nandroidx-navigation-ui = { group = \"androidx.navigation\", name = \"navigation-ui\", version.ref = \"nav\" }\nandroidx-preference = { module = \"androidx.preference:preference\", version = \"1.2.1\" }\nandroidx-recyclerview = { module = \"androidx.recyclerview:recyclerview\", version = \"1.4.0\" }\nandroidx-swiperefreshlayout = { module = \"androidx.swiperefreshlayout:swiperefreshlayout\", version = \"1.2.0\" }\n\nglide = { group = \"com.github.bumptech.glide\", name = \"glide\", version.ref = \"glide\" }\nglide-compiler = { group = \"com.github.bumptech.glide\", name = \"compiler\", version.ref = \"glide\" }\n\nokhttp = { group = \"com.squareup.okhttp3\", name = \"okhttp\", version.ref = \"okhttp\" }\nokhttp-dnsoverhttps = { group = \"com.squareup.okhttp3\", name = \"okhttp-dnsoverhttps\", version.ref = \"okhttp\" }\nokhttp-logging-interceptor = { group = \"com.squareup.okhttp3\", name = \"logging-interceptor\", version.ref = \"okhttp\" }\n\nagp-apksig = { group = \"com.android.tools.build\", name = \"apksig\", version.ref = \"agp\" }\nappiconloader = { module = \"me.zhanghai.android.appiconloader:appiconloader\", version = \"1.5.0\" }\nmaterial = { module = \"com.google.android.material:material\", version = \"1.12.0\" }\ngson = { module = \"com.google.code.gson:gson\", version = \"2.13.2\" }\nhiddenapibypass = { module = \"org.lsposed.hiddenapibypass:hiddenapibypass\", version = \"6.1\" }\nkotlin-stdlib = { module = \"org.jetbrains.kotlin:kotlin-stdlib\", version.ref = \"kotlin\" }\nkotlinx-coroutines-core = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-core\", version = \"1.10.2\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.3.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "## For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n#\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx1024m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n#\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\nandroid.useAndroidX=true\nandroid.nonFinalResIds=false\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 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# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\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\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    if ! command -v java >/dev/null 2>&1\n    then\n        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.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n\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\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# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "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@rem SPDX-License-Identifier: Apache-2.0\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\n@rem This is normally unused\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% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\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. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\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%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 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\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "hiddenapi/bridge/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "hiddenapi/bridge/build.gradle.kts",
    "content": "plugins { `java-library` }\n\ndependencies { compileOnly(projects.hiddenapi.stubs) }\n"
  },
  {
    "path": "hiddenapi/bridge/src/main/java/hidden/ByteBufferDexClassLoader.java",
    "content": "package hidden;\n\nimport java.nio.ByteBuffer;\n\nimport dalvik.system.BaseDexClassLoader;\n\npublic class ByteBufferDexClassLoader extends BaseDexClassLoader {\n\n    public ByteBufferDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {\n        super(dexFiles, parent);\n    }\n\n    public ByteBufferDexClassLoader(ByteBuffer[] dexFiles, String librarySearchPath, ClassLoader parent) {\n        super(dexFiles, librarySearchPath, parent);\n    }\n\n    public String getLdLibraryPath() {\n        return super.getLdLibraryPath();\n    }\n}\n"
  },
  {
    "path": "hiddenapi/bridge/src/main/java/hidden/HiddenApiBridge.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage hidden;\n\nimport android.app.ActivityManager;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInstaller;\nimport android.content.res.AssetManager;\nimport android.content.res.CompatibilityInfo;\nimport android.content.res.Resources;\nimport android.content.res.ResourcesImpl;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.UserHandle;\nimport android.system.ErrnoException;\nimport android.system.Int32Ref;\nimport android.system.Os;\nimport android.util.MutableInt;\n\nimport androidx.annotation.RequiresApi;\n\nimport java.io.File;\nimport java.io.FileDescriptor;\n\npublic class HiddenApiBridge {\n    public static int AssetManager_addAssetPath(AssetManager am, String path) {\n        return am.addAssetPath(path);\n    }\n\n    public static IBinder Binder_allowBlocking(IBinder binder) {\n        return Binder.allowBlocking(binder);\n    }\n\n    public static void Resources_setImpl(Resources resources, ResourcesImpl impl) {\n        resources.setImpl(impl);\n    }\n\n    public static int PackageInstaller_SessionParams_installFlags(PackageInstaller.SessionParams params) {\n        return params.installFlags;\n    }\n\n    public static void PackageInstaller_SessionParams_installFlags(PackageInstaller.SessionParams params, int flags) {\n        params.installFlags = flags;\n    }\n\n    public static IBinder Context_getActivityToken(Context ctx) {\n        return ctx.getActivityToken();\n    }\n\n    public static File Environment_getDataProfilesDePackageDirectory(int userId, String packageName) {\n        return Environment.getDataProfilesDePackageDirectory(userId, packageName);\n    }\n\n    public static Intent Context_registerReceiverAsUser(Context ctx, BroadcastReceiver receiver, UserHandle user,\n                                                        IntentFilter filter, String broadcastPermission, Handler scheduler) {\n\n        return ctx.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler);\n    }\n\n    public static UserHandle UserHandle_ALL() {\n        return UserHandle.ALL;\n    }\n\n    public static UserHandle UserHandle(int h) {\n        return new UserHandle(h);\n    }\n\n    public static String ApplicationInfo_credentialProtectedDataDir(ApplicationInfo applicationInfo) {\n        return applicationInfo.credentialProtectedDataDir;\n    }\n\n    public static void ApplicationInfo_credentialProtectedDataDir(ApplicationInfo applicationInfo, String dir) {\n        applicationInfo.credentialProtectedDataDir = dir;\n    }\n\n    public static String[] ApplicationInfo_resourceDirs(ApplicationInfo applicationInfo) {\n        return applicationInfo.resourceDirs;\n    }\n\n    public static void ApplicationInfo_resourceDirs(ApplicationInfo applicationInfo, String[] resourceDirs) {\n        applicationInfo.resourceDirs = resourceDirs;\n    }\n\n    @RequiresApi(31)\n    public static String[] ApplicationInfo_overlayPaths(ApplicationInfo applicationInfo) {\n        return applicationInfo.overlayPaths;\n    }\n\n    @RequiresApi(31)\n    public static void ApplicationInfo_overlayPaths(ApplicationInfo applicationInfo, String[] overlayPaths) {\n        applicationInfo.overlayPaths = overlayPaths;\n    }\n\n    public static CompatibilityInfo Resources_getCompatibilityInfo(Resources res) {\n        return res.getCompatibilityInfo();\n    }\n\n    public static int Os_ioctlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {\n            return Os.ioctlInt(fd, cmd, new MutableInt(arg));\n        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {\n            return Os.ioctlInt(fd, cmd, new Int32Ref(arg));\n        } else {\n            return Os.ioctlInt(fd, cmd);\n        }\n    }\n\n    public static int ActivityManager_UID_OBSERVER_GONE() {\n        return ActivityManager.UID_OBSERVER_GONE;\n    }\n\n    public static int ActivityManager_UID_OBSERVER_ACTIVE() {\n        return ActivityManager.UID_OBSERVER_ACTIVE;\n    }\n\n    public static int ActivityManager_UID_OBSERVER_IDLE() {\n        return ActivityManager.UID_OBSERVER_IDLE;\n    }\n\n    public static int ActivityManager_UID_OBSERVER_CACHED() {\n        return ActivityManager.UID_OBSERVER_CACHED;\n    }\n\n    public static int ActivityManager_PROCESS_STATE_UNKNOWN() {\n        return ActivityManager.PROCESS_STATE_UNKNOWN;\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "hiddenapi/stubs/build.gradle.kts",
    "content": "plugins { `java-library` }\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n    targetCompatibility = JavaVersion.VERSION_1_8\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/annotation/NonNull.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.ElementType.PARAMETER;\nimport static java.lang.annotation.RetentionPolicy.SOURCE;\n\n/**\n * Denotes that a parameter, field or method return value can never be null.\n * <p>\n * This is a marker annotation and it has no specific attributes.\n *\n * @paramDoc This value must never be {@code null}.\n * @returnDoc This value will never be {@code null}.\n * @hide\n */\n@Retention(SOURCE)\n@Target({METHOD, PARAMETER, FIELD})\npublic @interface NonNull {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/annotation/Nullable.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.ElementType.PARAMETER;\nimport static java.lang.annotation.RetentionPolicy.SOURCE;\n\n/**\n * Denotes that a parameter, field or method return value can be null.\n * <p>\n * When decorating a method call parameter, this denotes that the parameter can\n * legitimately be null and the method will gracefully deal with it. Typically\n * used on optional parameters.\n * <p>\n * When decorating a method, this denotes the method might legitimately return\n * null.\n * <p>\n * This is a marker annotation and it has no specific attributes.\n *\n * @paramDoc This value may be {@code null}.\n * @returnDoc This value may be {@code null}.\n */\n@Retention(SOURCE)\n@Target({METHOD, PARAMETER, FIELD})\npublic @interface Nullable {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/ActivityManager.java",
    "content": "package android.app;\n\npublic class ActivityManager {\n    public static int UID_OBSERVER_GONE;\n    public static int UID_OBSERVER_ACTIVE;\n    public static int UID_OBSERVER_IDLE;\n    public static int UID_OBSERVER_CACHED;\n    public static int PROCESS_STATE_UNKNOWN;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/ActivityThread.java",
    "content": "package android.app;\n\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.CompatibilityInfo;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.PersistableBundle;\n\npublic final class ActivityThread {\n    public static ActivityThread currentActivityThread() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    public ApplicationThread getApplicationThread() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n\n    public static Application currentApplication() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    public static String currentPackageName() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    public static String currentProcessName() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    public ContextImpl getSystemContext() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    public static ActivityThread systemMain() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n    private class ApplicationThread extends IApplicationThread.Stub {\n        @Override\n        public IBinder asBinder() {\n            return null;\n        }\n    }\n\n    public static final class ActivityClientRecord {\n        Bundle state;\n        PersistableBundle persistentState;\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/Application.java",
    "content": "package android.app;\n\npublic class Application {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/ContentProviderHolder.java",
    "content": "package android.app;\n\nimport android.content.IContentProvider;\n\npublic class ContentProviderHolder {\n    public IContentProvider provider;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/ContextImpl.java",
    "content": "package android.app;\n\nimport android.content.Context;\n\npublic class ContextImpl extends Context {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/IActivityController.java",
    "content": "package android.app;\n\nimport android.content.Intent;\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\npublic interface IActivityController extends IInterface {\n    /**\n     * The system is trying to start an activity.  Return true to allow\n     * it to be started as normal, or false to cancel/reject this activity.\n     */\n    boolean activityStarting(Intent intent, String pkg);\n\n    /**\n     * The system is trying to return to an activity.  Return true to allow\n     * it to be resumed as normal, or false to cancel/reject this activity.\n     */\n    boolean activityResuming(String pkg);\n\n    /**\n     * An application process has crashed (in Java).  Return true for the\n     * normal error recovery (app crash dialog) to occur, false to kill\n     * it immediately.\n     */\n    boolean appCrashed(String processName, int pid,\n                       String shortMsg, String longMsg,\n                       long timeMillis, String stackTrace);\n\n    /**\n     * Early call as soon as an ANR is detected.\n     */\n    int appEarlyNotResponding(String processName, int pid, String annotation);\n\n    /**\n     * An application process is not responding.  Return 0 to show the \"app\n     * not responding\" dialog, 1 to continue waiting, or -1 to kill it\n     * immediately.\n     */\n    int appNotResponding(String processName, int pid, String processStats);\n\n    /**\n     * The system process watchdog has detected that the system seems to be\n     * hung.  Return 1 to continue waiting, or -1 to let it continue with its\n     * normal kill.\n     */\n    int systemNotResponding(String msg);\n\n    /**\n     * 360 phones\n     */\n    boolean moveTaskToFront(String pkg, int task, int flags, Bundle options);\n\n    abstract class Stub extends Binder implements IActivityController {\n\n        public static IActivityController asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public IBinder asBinder() {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/IActivityManager.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed is distributed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.app;\n\nimport android.content.IIntentReceiver;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.UserInfo;\nimport android.content.res.Configuration;\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.RemoteException;\n\nimport androidx.annotation.RequiresApi;\n\npublic interface IActivityManager extends IInterface {\n    @RequiresApi(31)\n    int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,\n                                   Intent intent, String resolvedType, IIntentReceiver resultTo,\n                                   int resultCode, String resultData, Bundle resultExtras,\n                                   String[] requiredPermissions, String[] excludedPermissions,\n                                   String[] excludePackages, int appOp, Bundle bOptions,\n                                   boolean serialized, boolean sticky, int userId) throws RemoteException;\n\n    @RequiresApi(31)\n    int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,\n                                   Intent intent, String resolvedType, IIntentReceiver resultTo,\n                                   int resultCode, String resultData, Bundle resultExtras,\n                                   String[] requiredPermissions, String[] excludedPermissions,\n                                   int appOp, Bundle bOptions,\n                                   boolean serialized, boolean sticky, int userId) throws RemoteException;\n\n    @RequiresApi(30)\n    int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,\n                                   Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode,\n                                   String resultData, Bundle map, String[] requiredPermissions,\n                                   int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException;\n\n    int broadcastIntent(IApplicationThread caller, Intent intent,\n                        String resolvedType, IIntentReceiver resultTo, int resultCode,\n                        String resultData, Bundle map, String[] requiredPermissions,\n                        int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException;\n\n    int startActivity(IApplicationThread caller, String callingPackage, Intent intent,\n                      String resolvedType, IBinder resultTo, String resultWho, int requestCode,\n                      int flags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException;\n\n    @RequiresApi(30)\n    int startActivityWithFeature(IApplicationThread caller, String callingPackage,\n                                 String callingFeatureId, Intent intent, String resolvedType,\n                                 IBinder resultTo, String resultWho, int requestCode, int flags,\n                                 ProfilerInfo profilerInfo, Bundle options) throws RemoteException;\n\n    int startActivityAsUser(IApplicationThread caller, String callingPackage,\n                            Intent intent, String resolvedType, IBinder resultTo, String resultWho,\n                            int requestCode, int flags, ProfilerInfo profilerInfo,\n                            Bundle options, int userId) throws RemoteException;\n\n    @RequiresApi(30)\n    int startActivityAsUserWithFeature(IApplicationThread caller, String callingPackage,\n                                       String callingFeatureId, Intent intent, String resolvedType,\n                                       IBinder resultTo, String resultWho, int requestCode, int flags,\n                                       ProfilerInfo profilerInfo, Bundle options, int userId) throws RemoteException;\n\n    void forceStopPackage(String packageName, int userId) throws RemoteException;\n\n    boolean startUserInBackground(int userid) throws RemoteException;\n\n    Intent registerReceiver(IApplicationThread caller, String callerPackage,\n                            IIntentReceiver receiver, IntentFilter filter,\n                            String requiredPermission, int userId, int flags) throws RemoteException;\n\n    void finishReceiver(IBinder caller, int resultCode, String resultData,\n                        Bundle resultExtras, boolean resultAbort, int flags) throws RemoteException;\n\n    @RequiresApi(30)\n    Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,\n                                       String callingFeatureId, IIntentReceiver receiver, IntentFilter filter,\n                                       String requiredPermission, int userId, int flags) throws RemoteException;\n\n    @RequiresApi(31)\n    Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage, String callingFeatureId,\n                                       String receiverId, IIntentReceiver receiver, IntentFilter filter,\n                                       String requiredPermission, int userId, int flags) throws RemoteException;\n\n    int bindService(IApplicationThread caller, IBinder token, Intent service,\n                    String resolvedType, IServiceConnection connection, int flags,\n                    String callingPackage, int userId) throws RemoteException;\n\n    @RequiresApi(34)\n    int bindService(IApplicationThread caller, IBinder token, Intent service,\n                    String resolvedType, IServiceConnection connection, long flags,\n                    String callingPackage, int userId) throws RemoteException;\n\n    boolean unbindService(IServiceConnection connection) throws RemoteException;\n\n    boolean switchUser(int userid) throws RemoteException;\n\n    UserInfo getCurrentUser() throws RemoteException;\n\n    void setActivityController(IActivityController watcher, boolean imAMonkey) throws RemoteException;\n\n    @RequiresApi(29)\n    ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token, String tag) throws RemoteException;\n\n    ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token) throws RemoteException;\n\n    Configuration getConfiguration() throws RemoteException;\n\n    void registerUidObserver(IUidObserver observer, int which, int cutpoint, String callingPackage) throws RemoteException;\n\n    abstract class Stub extends Binder implements IActivityManager {\n        public static int TRANSACTION_setActivityController;\n\n        public static IActivityManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/IApplicationThread.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.app;\n\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\npublic interface IApplicationThread extends IInterface {\n    abstract class Stub extends Binder implements IApplicationThread {\n        public static IApplicationThread asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/INotificationManager.java",
    "content": "package android.app;\n\nimport android.content.pm.ParceledListSlice;\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.RemoteException;\n\nimport androidx.annotation.RequiresApi;\n\npublic interface INotificationManager extends IInterface {\n    void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,\n                                    Notification notification, int userId) throws RemoteException;\n\n    void cancelNotificationWithTag(String pkg, String tag, int id, int userId) throws RemoteException;\n\n    @RequiresApi(30)\n    void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id, int userId) throws RemoteException;\n\n    void createNotificationChannelsForPackage(String pkg, int uid, ParceledListSlice<NotificationChannel> channelsList) throws RemoteException;\n\n    void updateNotificationChannelForPackage(String pkg, int uid, NotificationChannel channel);\n\n    @RequiresApi(30)\n    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted) throws RemoteException;\n\n    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted) throws RemoteException;\n\n    abstract class Stub extends Binder implements INotificationManager {\n        public static INotificationManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java",
    "content": "package android.app;\n\nimport android.content.ComponentName;\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\npublic interface IServiceConnection extends IInterface {\n    void connected(ComponentName name, IBinder service, boolean dead);\n\n    abstract class Stub extends Binder implements IServiceConnection {\n\n        public static IServiceConnection asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public IBinder asBinder() {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/IUidObserver.java",
    "content": "package android.app;\n\nimport android.os.Binder;\n\npublic interface IUidObserver {\n\n    void onUidGone(int uid, boolean disabled);\n\n    void onUidActive(int uid);\n\n    void onUidIdle(int uid, boolean disabled);\n\n    void onUidCachedChanged(int uid, boolean cached);\n\n    abstract class Stub extends Binder implements IUidObserver {\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/LoadedApk.java",
    "content": "package android.app;\n\nimport android.content.pm.ApplicationInfo;\n\npublic final class LoadedApk {\n\tprivate ClassLoader mDefaultClassLoader;\n\n\tpublic ApplicationInfo getApplicationInfo() {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic ClassLoader getClassLoader() {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic String getPackageName() {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic String getResDir() {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/Notification.java",
    "content": "package android.app;\n\npublic class Notification {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/NotificationChannel.java",
    "content": "package android.app;\n\npublic class NotificationChannel {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/ProfilerInfo.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.app;\n\npublic class ProfilerInfo {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/app/ResourcesManager.java",
    "content": "package android.app;\n\npublic class ResourcesManager {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/AttributionSource.java",
    "content": "package android.content;\n\npublic class AttributionSource {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/BroadcastReceiver.java",
    "content": "package android.content;\n\npublic abstract class BroadcastReceiver {\n    public abstract void onReceive(Context context, Intent intent);\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/ComponentName.java",
    "content": "package android.content;\n\npublic final class ComponentName {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/Context.java",
    "content": "package android.content;\n\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.UserHandle;\n\npublic class Context {\n    public IBinder getActivityToken() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,\n                                         IntentFilter filter, String broadcastPermission, Handler scheduler) {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/IContentProvider.java",
    "content": "package android.content;\n\nimport android.os.Bundle;\nimport android.os.IInterface;\nimport android.os.RemoteException;\n\nimport androidx.annotation.RequiresApi;\n\npublic interface IContentProvider extends IInterface {\n    Bundle call(String callingPkg, String method,\n                String arg, Bundle extras) throws RemoteException;\n\n    @RequiresApi(29)\n    Bundle call(String callingPkg, String authority, String method,\n                String arg, Bundle extras) throws RemoteException;\n\n    @RequiresApi(30)\n    Bundle call(String callingPkg, String attributionTag, String authority,\n                String method, String arg, Bundle extras) throws RemoteException;\n\n    @RequiresApi(31)\n    Bundle call(AttributionSource attributionSource, String authority,\n                String method, String arg, Bundle extras) throws RemoteException;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/IIntentReceiver.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.content;\n\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\npublic interface IIntentReceiver extends IInterface {\n    void performReceive(Intent intent, int resultCode, String data,\n                        Bundle extras, boolean ordered, boolean sticky, int sendingUser);\n    abstract class Stub extends Binder implements IIntentReceiver {\n        public static IIntentReceiver asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public IBinder asBinder() {\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/IIntentSender.java",
    "content": "package android.content;\n\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport androidx.annotation.RequiresApi;\n\npublic interface IIntentSender extends IInterface {\n\n    int send(int code, Intent intent, String resolvedType,\n             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);\n\n    @RequiresApi(26)\n    void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,\n              IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);\n\n    abstract class Stub extends Binder implements IIntentSender {\n\n        public Stub() {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public android.os.IBinder asBinder() {\n            throw new UnsupportedOperationException();\n        }\n\n        public static IIntentSender asInterface(IBinder binder) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/Intent.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.content;\n\npublic class Intent {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/IntentFilter.java",
    "content": "package android.content;\n\npublic class IntentFilter {\n\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/IntentSender.java",
    "content": "package android.content;\n\npublic class IntentSender {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/ApplicationInfo.java",
    "content": "package android.content.pm;\n\nimport androidx.annotation.RequiresApi;\n\npublic class ApplicationInfo {\n    public String credentialProtectedDataDir;\n\n    public String[] resourceDirs;\n\n    @RequiresApi(31)\n    public String[] overlayPaths;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/BaseParceledListSlice.java",
    "content": "package android.content.pm;\n\nimport java.util.List;\n\nabstract class BaseParceledListSlice<T> {\n\n    public List<T> getList() {\n        throw new RuntimeException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/IPackageInstaller.java",
    "content": "package android.content.pm;\n\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\npublic interface IPackageInstaller extends IInterface {\n\n    void uninstall(android.content.pm.VersionedPackage versionedPackage, java.lang.String callerPackageName, int flags, android.content.IntentSender statusReceiver, int userId) throws android.os.RemoteException;\n\n    abstract class Stub extends Binder implements IPackageInstaller {\n        public static IPackageInstaller asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/IPackageManager.java",
    "content": "package android.content.pm;\n\nimport android.content.Intent;\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.RemoteException;\n\nimport androidx.annotation.RequiresApi;\n\nimport java.util.List;\n\npublic interface IPackageManager extends IInterface {\n\n    boolean isPackageAvailable(String packageName, int userId) throws RemoteException;\n\n    boolean getApplicationHiddenSettingAsUser(String packageName, int userId) throws RemoteException;\n\n    ApplicationInfo getApplicationInfo(String packageName, int flags, int userId)\n            throws RemoteException;\n\n    @RequiresApi(33)\n    ApplicationInfo getApplicationInfo(String packageName, long flags, int userId)\n            throws RemoteException;\n\n    PackageInfo getPackageInfo(String packageName, int flags, int userId)\n            throws RemoteException;\n\n    @RequiresApi(33)\n    PackageInfo getPackageInfo(String packageName, long flags, int userId)\n            throws RemoteException;\n\n    int getPackageUid(String packageName, int flags, int userId) throws RemoteException;\n\n    @RequiresApi(33)\n    int getPackageUid(String packageName, long flags, int userId) throws RemoteException;\n\n    String[] getPackagesForUid(int uid)\n            throws RemoteException;\n\n    ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId)\n            throws RemoteException;\n\n    @RequiresApi(33)\n    ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId)\n            throws RemoteException;\n\n    ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId)\n            throws RemoteException;\n\n    @RequiresApi(33)\n    ParceledListSlice<ApplicationInfo> getInstalledApplications(long flags, int userId)\n            throws RemoteException;\n\n    int getUidForSharedUser(String sharedUserName)\n            throws RemoteException;\n\n    void grantRuntimePermission(String packageName, String permissionName, int userId)\n            throws RemoteException;\n\n    void revokeRuntimePermission(String packageName, String permissionName, int userId)\n            throws RemoteException;\n\n    int getPermissionFlags(String permissionName, String packageName, int userId)\n            throws RemoteException;\n\n    void updatePermissionFlags(String permissionName, String packageName, int flagMask, int flagValues, int userId)\n            throws RemoteException;\n\n    int checkPermission(String permName, String pkgName, int userId)\n            throws RemoteException;\n\n    int checkUidPermission(String permName, int uid)\n            throws RemoteException;\n\n    IPackageInstaller getPackageInstaller() throws RemoteException;\n\n    int installExistingPackageAsUser(String packageName, int userId, int installFlags,\n                                     int installReason) throws RemoteException;\n\n    @RequiresApi(29)\n    int installExistingPackageAsUser(String packageName, int userId, int installFlags,\n                                     int installReason, List<String> whiteListedPermissions) throws RemoteException;\n\n    ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,\n                                                         String resolvedType, int flags, int userId) throws RemoteException;\n\n    @RequiresApi(33)\n    ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,\n                                            String resolvedType, long flags, int userId) throws RemoteException;\n\n    boolean performDexOptMode(String packageName, boolean checkProfiles,\n                              String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)\n            throws RemoteException;\n\n    void clearApplicationProfileData(String packageName) throws RemoteException;\n\n    abstract class Stub extends Binder implements IPackageManager {\n\n        public static IPackageManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/PackageInfo.java",
    "content": "package android.content.pm;\n\npublic class PackageInfo {\n\n    public String overlayTarget;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/PackageInstaller.java",
    "content": "package android.content.pm;\n\npublic class PackageInstaller {\n    public static class SessionParams {\n        public int installFlags = 0;\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/PackageManager.java",
    "content": "package android.content.pm;\n\nimport java.util.List;\n\npublic class PackageManager {\n    public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/PackageParser.java",
    "content": "package android.content.pm;\n\nimport java.io.File;\n\npublic class PackageParser {\n\tpublic static class PackageLite {\n\t\tpublic final String packageName = null;\n\t}\n\n\tpublic final static class Package {\n        public ApplicationInfo applicationInfo;\n\t}\n\n\t/** Before SDK21 */\n\tpublic static PackageLite parsePackageLite(String packageFile, int flags) {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\t/** Since SDK21 */\n\tpublic static PackageLite parsePackageLite(File packageFile, int flags) throws PackageParserException {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic Package parsePackage(File packageFile, int flags, boolean useCaches)\n\t\t\tthrows PackageParserException {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\t/** Since SDK21 */\n\tpublic static class PackageParserException extends Exception {\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/ParceledListSlice.java",
    "content": "package android.content.pm;\n\nimport java.util.List;\n\npublic class ParceledListSlice<T> extends BaseParceledListSlice<T> {\n    public ParceledListSlice(List<T> list) {\n        throw new IllegalArgumentException(\"STUB\");\n    }\n\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/ResolveInfo.java",
    "content": "package android.content.pm;\n\npublic class ResolveInfo {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/UserInfo.java",
    "content": "package android.content.pm;\n\npublic class UserInfo {\n    public int id;\n    public String name;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/pm/VersionedPackage.java",
    "content": "package android.content.pm;\n\npublic class VersionedPackage {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/AssetManager.java",
    "content": "package android.content.res;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic final class AssetManager {\n\tpublic final int addAssetPath(String path) {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic void close() {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic final InputStream open(String fileName) throws IOException {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/CompatibilityInfo.java",
    "content": "package android.content.res;\n\npublic class CompatibilityInfo {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/Configuration.java",
    "content": "package android.content.res;\n\npublic class Configuration {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/Resources.java",
    "content": "package android.content.res;\n\nimport android.util.DisplayMetrics;\n\npublic class Resources {\n\tpublic Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic Resources(ClassLoader classLoader) {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic void setImpl(ResourcesImpl impl) {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n\n\tpublic CompatibilityInfo getCompatibilityInfo() {\n\t    throw new UnsupportedOperationException(\"STUB\");\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/ResourcesImpl.java",
    "content": "package android.content.res;\n\npublic class ResourcesImpl {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/ResourcesKey.java",
    "content": "package android.content.res;\n\npublic class ResourcesKey {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/content/res/TypedArray.java",
    "content": "package android.content.res;\n\npublic class TypedArray {\n\tprotected TypedArray(Resources resources) {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/ddm/DdmHandleAppName.java",
    "content": "package android.ddm;\n\npublic class DdmHandleAppName {\n    public static void setAppName(String name, int userId) {\n        throw new RuntimeException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/graphics/Movie.java",
    "content": "package android.graphics;\n\npublic class Movie {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/graphics/drawable/Drawable.java",
    "content": "package android.graphics.drawable;\n\npublic class Drawable {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Binder.java",
    "content": "package android.os;\n\nimport android.annotation.NonNull;\nimport android.annotation.Nullable;\n\nimport java.io.FileDescriptor;\n\npublic class Binder implements IBinder {\n\n    @Override\n    public boolean transact(int code, @NonNull Parcel data, Parcel reply, int flags) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public String getInterfaceDescriptor() {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    public boolean pingBinder() {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public boolean isBinderAlive() {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public IInterface queryLocalInterface(@NonNull String descriptor) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public void dump(@NonNull FileDescriptor fd, String[] args) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public void dumpAsync(@NonNull FileDescriptor fd, String[] args) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    @Override\n    public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,\n                                 int flags) throws RemoteException {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    public static IBinder allowBlocking(IBinder binder){\n        throw new RuntimeException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Build.java",
    "content": "package android.os;\n\npublic class Build {\n    public static class VERSION {\n        public final static int SDK_INT = SystemProperties.getInt(\n                \"ro.build.version.sdk\", 0);\n    }\n    public static class VERSION_CODES {\n        public static final int O_MR1 = 27;\n        public static final int P = 28;\n        public static final int Q = 29;\n        public static final int R = 30;\n        public static final int S = 31;\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Bundle.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage android.os;\n\npublic class Bundle {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Environment.java",
    "content": "package android.os;\n\nimport java.io.File;\n\npublic class Environment {\n    public static File getDataProfilesDePackageDirectory(int userId, String packageName) {\n        throw new IllegalArgumentException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Handler.java",
    "content": "package android.os;\n\npublic class Handler {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/IBinder.java",
    "content": "package android.os;\n\nimport android.annotation.NonNull;\nimport android.annotation.Nullable;\n\nimport java.io.FileDescriptor;\n\npublic interface IBinder {\n\n    boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags);\n\n    @Nullable\n    String getInterfaceDescriptor();\n\n    boolean pingBinder();\n\n    boolean isBinderAlive();\n\n    @Nullable\n    IInterface queryLocalInterface(@NonNull String descriptor);\n\n    void dump(@NonNull FileDescriptor fd, @Nullable String[] args);\n\n    void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args);\n\n    void linkToDeath(@NonNull DeathRecipient recipient, int flags);\n\n    boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);\n\n    interface DeathRecipient {\n        void binderDied();\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/IInterface.java",
    "content": "package android.os;\n\npublic interface IInterface {\n\n    IBinder asBinder();\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/IPowerManager.java",
    "content": "package android.os;\n\npublic interface IPowerManager extends IInterface {\n    void reboot(boolean confirm, String reason, boolean wait) throws RemoteException;\n\n    abstract class Stub extends Binder implements IPowerManager {\n\n        public static IPowerManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/IServiceCallback.java",
    "content": "package android.os;\npublic interface IServiceCallback extends IInterface\n{\n    public static abstract class Stub extends android.os.Binder implements android.os.IServiceCallback {\n    }\n    /**\n     * Called when a service is registered.\n     *\n     * @param name the service name that has been registered with\n     * @param binder the binder that is registered\n     */\n    public void onRegistration(java.lang.String name, android.os.IBinder binder) throws android.os.RemoteException;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/IServiceManager.java",
    "content": "package android.os;\n\npublic interface IServiceManager extends IInterface {\n\n    void tryUnregisterService(java.lang.String name, android.os.IBinder service);\n\n    IBinder getService(String name);\n\n    public void registerForNotifications(String name, IServiceCallback cb);\n\n    abstract class Stub extends Binder implements IServiceManager {\n        public static IServiceManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/IUserManager.java",
    "content": "package android.os;\n\nimport android.content.pm.UserInfo;\n\nimport androidx.annotation.RequiresApi;\n\nimport java.util.List;\n\npublic interface IUserManager extends IInterface {\n    @RequiresApi(26)\n    boolean isUserUnlocked(int userId)\n            throws RemoteException;\n\n    List<UserInfo> getUsers(boolean excludeDying)\n            throws RemoteException;\n\n    List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated)\n            throws RemoteException;\n\n    UserInfo getUserInfo(int userHandle) throws RemoteException;\n\n    UserInfo getProfileParent(int userId) throws RemoteException;\n\n    boolean isUserUnlockingOrUnlocked(int userId) throws RemoteException;\n\n    abstract class Stub extends Binder implements IUserManager {\n\n        public static IUserManager asInterface(IBinder obj) {\n            throw new RuntimeException(\"STUB\");\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Parcel.java",
    "content": "package android.os;\n\npublic class Parcel {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/Parcelable.java",
    "content": "package android.os;\n\npublic interface Parcelable {\n    interface Creator<T>{\n        public T createFromParcel(Parcel source);\n        public T[] newArray(int size);\n    }\n    void writeToParcel(Parcel dest, int flags);\n    int describeContents();\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/PersistableBundle.java",
    "content": "package android.os;\n\npublic class PersistableBundle {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/RemoteException.java",
    "content": "package android.os;\n\npublic class RemoteException extends Exception {\n\n    public RemoteException(String message) {\n        throw new RuntimeException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/ResultReceiver.java",
    "content": "package android.os;\n\npublic class ResultReceiver {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/SELinux.java",
    "content": "package android.os;\n\npublic class SELinux {\n    public static boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static boolean setFileContext(String path, String context) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static String getFileContext(String path) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static boolean setFSCreateContext(String context){\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/ServiceManager.java",
    "content": "package android.os;\n\nimport android.annotation.Nullable;\n\npublic class ServiceManager {\n\n    /**\n     * Returns a reference to a service with the given name.\n     *\n     * @param name the name of the service to get\n     * @return a reference to the service, or <code>null</code> if the service doesn't exist\n     */\n    @Nullable\n    public static IBinder getService(String name) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Place a new @a service called @a name into the service\n     * manager.\n     *\n     * @param name the name of the new service\n     * @param service the service object\n     */\n    public static void addService(String name, IBinder service) {\n        throw new RuntimeException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/ShellCallback.java",
    "content": "package android.os;\n\npublic class ShellCallback implements Parcelable {\n    public static final Parcelable.Creator<ShellCallback> CREATOR = new Creator<ShellCallback>() {\n        @Override\n        public ShellCallback createFromParcel(Parcel source) {\n            throw new IllegalArgumentException(\"STUB\");\n        }\n\n        @Override\n        public ShellCallback[] newArray(int size) {\n            throw new IllegalArgumentException(\"STUB\");\n        }\n    };\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        throw new IllegalArgumentException(\"STUB\");\n    }\n\n    @Override\n    public int describeContents() {\n        throw new IllegalArgumentException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/ShellCommand.java",
    "content": "package android.os;\n\nimport java.io.FileDescriptor;\nimport java.io.InputStream;\nimport java.io.PrintWriter;\n\npublic abstract class ShellCommand {\n    public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,\n                    String[] args, ShellCallback callback, ResultReceiver resultReceiver) {\n        throw new IllegalArgumentException(\"STUB!\");\n    }\n\n    public abstract int onCommand(String cmd);\n    public abstract void onHelp();\n\n    public String getNextOption(){\n        throw new IllegalArgumentException(\"STUB!\");\n    }\n\n    public String getNextArgRequired() {\n        throw new IllegalArgumentException(\"STUB!\");\n    }\n\n    public PrintWriter getErrPrintWriter() {\n        throw new IllegalArgumentException(\"STUB!\");\n    }\n    public PrintWriter getOutPrintWriter() {\n        throw new IllegalArgumentException(\"STUB!\");\n    }\n    public InputStream getRawInputStream() {\n        throw new IllegalArgumentException(\"STUB!\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/SystemProperties.java",
    "content": "package android.os;\n\nimport android.annotation.NonNull;\nimport android.annotation.Nullable;\n\npublic class SystemProperties {\n    public static String get(@NonNull String key) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static String get(@NonNull String key, @Nullable String def) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static void set(@NonNull String key, @Nullable String val) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static boolean getBoolean(@NonNull String key, boolean def) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n\n    public static int getInt(@NonNull String key, int def) {\n        throw new UnsupportedOperationException(\"Stub\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/UserHandle.java",
    "content": "package android.os;\n\nimport android.annotation.NonNull;\n\npublic class UserHandle {\n\n    public UserHandle(int h) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    public int getIdentifier() {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    public static final @NonNull\n    UserHandle ALL = null;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/os/UserManager.java",
    "content": "package android.os;\n\nimport android.content.pm.UserInfo;\n\nimport java.util.List;\n\npublic class UserManager {\n    public List<UserInfo> getUsers() {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/permission/IPermissionManager.java",
    "content": "package android.permission;\n\nimport java.util.List;\n\npublic interface IPermissionManager {\n\n    List<?> getSplitPermissions();\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/system/ErrnoException.java",
    "content": "package android.system;\n\npublic final class ErrnoException extends Exception {\n\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/system/Int32Ref.java",
    "content": "package android.system;\n\nimport java.util.Objects;\n\npublic class Int32Ref {\n    public int value;\n\n    public Int32Ref(int value) {\n        this.value = value;\n    }\n\n    @Override public String toString() {\n        return Objects.toString(this);\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/system/Os.java",
    "content": "package android.system;\n\nimport android.util.MutableInt;\n\nimport androidx.annotation.RequiresApi;\n\nimport java.io.FileDescriptor;\n\npublic class Os {\n    public static int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException {\n        throw new ErrnoException();\n    }\n\n    @RequiresApi(27)\n    public static int ioctlInt(FileDescriptor fd, int cmd, Int32Ref arg) throws ErrnoException {\n        throw new ErrnoException();\n    }\n\n    @RequiresApi(31)\n    public static int ioctlInt(FileDescriptor fd, int cmd) throws ErrnoException {\n        throw new ErrnoException();\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/util/DisplayMetrics.java",
    "content": "package android.util;\n\npublic class DisplayMetrics {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/util/MutableInt.java",
    "content": "package android.util;\n\npublic final class MutableInt {\n    public int value;\n\n    public MutableInt(int value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/util/TypedValue.java",
    "content": "package android.util;\n\npublic class TypedValue {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/view/IWindowManager.java",
    "content": "package android.view;\n\nimport android.app.IActivityManager;\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\npublic interface IWindowManager extends IInterface {\n    void lockNow(Bundle options);\n\n    abstract class Stub extends Binder implements IWindowManager {\n        public static IWindowManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/webkit/WebViewDelegate.java",
    "content": "package android.webkit;\n\npublic class WebViewDelegate {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/webkit/WebViewFactory.java",
    "content": "package android.webkit;\n\npublic class WebViewFactory {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/android/webkit/WebViewFactoryProvider.java",
    "content": "package android.webkit;\n\npublic class WebViewFactoryProvider {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/androidx/annotation/IntRange.java",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage androidx.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.ANNOTATION_TYPE;\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.LOCAL_VARIABLE;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.ElementType.PARAMETER;\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\n/**\n * Denotes that the annotated element should be an int or long in the given range\n * <p>\n * Example:\n * <pre><code>\n *  &#64;IntRange(from=0,to=255)\n *  public int getAlpha() {\n *      ...\n *  }\n * </code></pre>\n */\n@Retention(CLASS)\n@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE})\npublic @interface IntRange {\n    /**\n     * Smallest value, inclusive\n     */\n    long from() default Long.MIN_VALUE;\n\n    /**\n     * Largest value, inclusive\n     */\n    long to() default Long.MAX_VALUE;\n}"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/androidx/annotation/RequiresApi.java",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage androidx.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.CONSTRUCTOR;\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.ElementType.TYPE;\nimport static java.lang.annotation.RetentionPolicy.SOURCE;\n\n/**\n * Denotes that the annotated element should only be called on the given API level\n * or higher.\n * <p>\n * This is similar in purpose to the older {@code @TargetApi} annotation, but more\n * clearly expresses that this is a requirement on the caller, rather than being\n * used to \"suppress\" warnings within the method that exceed the {@code minSdkVersion}.\n */\n@Retention(SOURCE)\n@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})\npublic @interface RequiresApi {\n\n    /**\n     * The API level to require. Alias for {@link #api} which allows you to leave out the\n     * {@code api=} part.\n     */\n    @IntRange(from = 1)\n    int value() default 1;\n\n    /**\n     * The API level to require\n     */\n    @IntRange(from = 1)\n    int api() default 1;\n}"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/internal/os/BinderInternal.java",
    "content": "package com.android.internal.os;\n\nimport android.os.IBinder;\n\npublic class BinderInternal {\n    public static final native IBinder getContextObject();\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/internal/os/ZygoteInit.java",
    "content": "package com.android.internal.os;\n\npublic class ZygoteInit {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/internal/util/XmlUtils.java",
    "content": "package com.android.internal.util;\n\nimport org.xmlpull.v1.XmlPullParserException;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\n\npublic class XmlUtils {\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic static final HashMap readMapXml(InputStream in) throws XmlPullParserException, IOException {\n\t\tthrow new UnsupportedOperationException(\"STUB\");\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/server/LocalServices.java",
    "content": "package com.android.server;\n\npublic class LocalServices {\n\n    public static <T> T getService(Class<T> type) {\n        throw new UnsupportedOperationException(\"STUB\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/server/SystemService.java",
    "content": "package com.android.server;\n\npublic abstract class SystemService {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/server/SystemServiceManager.java",
    "content": "package com.android.server;\n\nimport java.util.ArrayList;\n\npublic class SystemServiceManager {\n    private final ArrayList<SystemService> mServices = new ArrayList<>();\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/server/am/ActivityManagerService.java",
    "content": "package com.android.server.am;\n\nimport com.android.server.SystemService;\n\npublic class ActivityManagerService {\n    public static final class Lifecycle extends SystemService {\n        public ActivityManagerService getService() {\n            throw new UnsupportedOperationException(\"STUB\");\n        }\n        private ProcessRecord findProcessLocked(String process, int userId, String callName) {\n            throw new UnsupportedOperationException(\"STUB\");\n        }\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/com/android/server/am/ProcessRecord.java",
    "content": "package com.android.server.am;\n\npublic class ProcessRecord {\n    String processName = null;\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/dalvik/system/BaseDexClassLoader.java",
    "content": "package dalvik.system;\n\nimport java.nio.ByteBuffer;\n\npublic class BaseDexClassLoader extends ClassLoader {\n    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public BaseDexClassLoader(ByteBuffer[] dexFiles, String librarySearchPath, ClassLoader parent) {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    public String getLdLibraryPath() {\n        throw new RuntimeException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/dalvik/system/VMRuntime.java",
    "content": "package dalvik.system;\n\npublic class VMRuntime {\n\n    public static VMRuntime getRuntime() {\n        throw new RuntimeException(\"Stub!\");\n    }\n\n    // Use `Process.is64Bit()` instead\n    public native boolean is64Bit();\n\n    public native String vmInstructionSet();\n\n    public native boolean isJavaDebuggable();\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/org/xmlpull/v1/XmlPullParserException.java",
    "content": "package org.xmlpull.v1;\n\npublic class XmlPullParserException extends Throwable {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/sun/misc/CompoundEnumeration.java",
    "content": "package sun.misc;\n\nimport java.util.Enumeration;\nimport java.util.NoSuchElementException;\n\npublic class CompoundEnumeration<E> implements Enumeration<E> {\n    private final Enumeration<E>[] enums;\n    private int index = 0;\n\n    public CompoundEnumeration(Enumeration<E>[] enums) {\n        this.enums = enums;\n    }\n\n    private boolean next() {\n        while (index < enums.length) {\n            if (enums[index] != null && enums[index].hasMoreElements()) {\n                return true;\n            }\n            index++;\n        }\n        return false;\n    }\n\n    public boolean hasMoreElements() {\n        return next();\n    }\n\n    public E nextElement() {\n        if (!next()) {\n            throw new NoSuchElementException();\n        }\n        return enums[index].nextElement();\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/sun/net/www/ParseUtil.java",
    "content": "package sun.net.www;\n\npublic class ParseUtil {\n    public static String encodePath(String path, boolean flag) {\n        throw new RuntimeException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/sun/net/www/protocol/jar/Handler.java",
    "content": "package sun.net.www.protocol.jar;\n\npublic abstract class Handler extends java.net.URLStreamHandler {\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/xposed/dummy/XResourcesSuperClass.java",
    "content": "package xposed.dummy;\n\nimport android.content.res.Resources;\n\n/**\n * This class is used as super class of XResources.\n *\n * This implementation isn't included in the .dex file. Instead, it's created on the device.\n * Usually, it will extend Resources, but some ROMs use their own Resources subclass.\n * In that case, XResourcesSuperClass will extend the ROM's subclass in an attempt to increase\n * compatibility.\n */\npublic class XResourcesSuperClass extends Resources {\n\t/** Dummy, will never be called (objects are transferred to this class only). */\n\tprotected XResourcesSuperClass() {\n\t\tsuper(null, null, null);\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tprotected XResourcesSuperClass(ClassLoader classLoader) {\n\t\tsuper(classLoader);\n\t\tthrow new UnsupportedOperationException();\n\t}\n}\n"
  },
  {
    "path": "hiddenapi/stubs/src/main/java/xposed/dummy/XTypedArraySuperClass.java",
    "content": "package xposed.dummy;\n\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\n\n/**\n * This class is used as super class of XResources.XTypedArray.\n *\n * This implementation isn't included in the .dex file. Instead, it's created on the device.\n * Usually, it will extend TypedArray, but some ROMs use their own TypedArray subclass.\n * In that case, XTypedArraySuperClass will extend the ROM's subclass in an attempt to increase\n * compatibility.\n */\npublic class XTypedArraySuperClass extends TypedArray {\n\t/** Dummy, will never be called (objects are transferred to this class only). */\n\tprotected XTypedArraySuperClass(Resources resources) {\n\t\tsuper(resources);\n\t\tthrow new UnsupportedOperationException();\n\t}\n}\n"
  },
  {
    "path": "magisk-loader/update/changelog.md",
    "content": "# LSPosed v1.11.0 🎐\n\nThis release brings major improvements for **Android 16 Beta** readiness, resolves specific quirks on Android 10 and OnePlus devices, and significantly reinforces overall system stability.\n\n### 📱 Compatibility & Core\n*   **Android 16 Beta Support:** Fixed compatibility issues with Android 16 QPR Beta 3 (specifically `UserManager` changes) and recent ART updates affecting the `dex2oat` wrapper.\n*   **Android 10 Fixes:** Resolved `dex2oat` crashes caused by 32-bit/64-bit architecture mismatches.\n*   **OnePlus Compatibility:** Restored `Application#attach` hooking capabilities, overcoming aggressive method inlining found in recent OOS updates.\n*   **Dex2Oat Overhaul:** Refactored the wrapper to utilize the APEX linker directly, eliminating missing symbol errors and boosting reliability.\n\n### 🛠️ Stability & Fixes\n*   **Database Integrity:** Resolved critical crashes and potential corruption during database initialization and migration.\n*   **Frida Compatibility:** Fixed `SIGSEGV` crashes when running alongside Frida by making memory mapping parsing more robust.\n*   **SELinux:** Corrected file contexts for the modern Xposed API 100 (`openRemoteFile`) and ensured they persist across reboots.\n*   **Injection Reliability:** Implemented retry logic for System Server injection to minimize start-up failures.\n\n### ⚡ Internal Changes\n*   **Kotlin Refactor:** The `DexParser` has been rewritten in Kotlin for improved performance and maintainability.\n*   **WebUI Removal:** Removed the WebUI integration as it is no longer required.\n\n---\n\n## 🔮 Development Plan\n\nThe current LSPosed fork is undergoing a complete refactor into a new project: **Vector**. \n\nWe are in the process of rewriting the Java layer into Kotlin and adding extensive documentation for the native layer. \n\nThe name **Vector** was chosen to manifest its close mathematical relationship with **Matrix**, while symbolizing the framework's role as a precise injection vector for modules.\n"
  },
  {
    "path": "magisk-loader/update/zygisk.json",
    "content": "{\n\t\"version\": \"v1.11.0\",\n\t\"versionCode\": 7209,\n\t\"zipUrl\": \"https://github.com/JingMatrix/LSPosed/releases/download/v1.11.0/LSPosed-v1.11.0-7209-zygisk-release.zip\",\n\t\"changelog\": \"https://raw.githubusercontent.com/JingMatrix/LSPosed/master/magisk-loader/update/changelog.md\"\n}\n"
  },
  {
    "path": "native/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\nproject(native)\n\nset(CMAKE_CXX_STANDARD 23)\n\nadd_subdirectory(${VECTOR_ROOT}/external external)\n\nfile(GLOB_RECURSE NATIVE_SOURCES \"src/*.cpp\")\nadd_library(${PROJECT_NAME} STATIC ${NATIVE_SOURCES})\n\nset(IGNORED_WARNINGS\n    -Wno-c99-extensions\n    -Wno-extra-semi\n    -Wno-gnu-flexible-array-initializer\n    -Wno-gnu-string-literal-operator-templat\n    -Wno-gnu-zero-variadic-macro-arguments\n    -Wno-variadic-macros\n    -Wno-zero-length-array\n)\n\n# --- Include Directories ---\n# Add the 'include' directory as a PUBLIC include path. This means any\n# target that links against vector_native will also inherit this include path.\ntarget_include_directories(${PROJECT_NAME} PUBLIC include)\n\n# Add the 'src' directory and external XZ library includes as PRIVATE.\n# These are only needed to build vector_native itself.\ntarget_include_directories(${PROJECT_NAME} PRIVATE src ${VECTOR_ROOT}/external/xz-embedded/linux/include)\n\n# --- Compiler Options ---\n# Apply the standard pedantic warnings and the ignored warnings list.\ntarget_compile_options(${PROJECT_NAME} PRIVATE -Wpedantic ${IGNORED_WARNINGS})\n\n# --- Library Linking ---\n# Link against required external and system libraries.\n# PUBLIC libraries are propagated to targets that link against this one.\n# PRIVATE libraries are only used for the compilation of this target.\ntarget_link_libraries(${PROJECT_NAME} PUBLIC\n    dobby_static\n    lsplant_static\n    xz_static\n    log # Android logging library\n    fmt-header-only\n)\n\ntarget_link_libraries(${PROJECT_NAME} PRIVATE\n    dex_builder_static\n)\n"
  },
  {
    "path": "native/README.md",
    "content": "# Vector Native Library (`native`)\n\n## Purpose and Design Philosophy\n\nThis library provides low-level hooking and modification capabilities for the Android OS.\nIt is not a standalone application but a collection of components designed to be integrated into a larger loading mechanism, such as a Zygisk module.\n\n## Architectural Breakdown\n\nThe library is organized into distinct modules, each with a clear responsibility.\n\n### `core` - The Abstract Engine\n\nThis module defines the central abstractions and manages the runtime state. It's the conceptual heart of the library.\n\n-   **`Context`**: An abstract base class that defines the injection lifecycle. It contains pure virtual methods like `LoadDex` and `SetupEntryClass`. The consumer of this library (e.g., the Zygisk module) must inherit from `Context` and provide the concrete implementations for these steps.\n-   **`ConfigBridge`**: A simple, native-side singleton that acts as a cache for configuration data (specifically, the obfuscation map) that is fetched and provided by the consumer.\n-   **`native_api`**: Implements the native module support system. It works by hooking the system's `do_dlopen` function. When it detects a registered module library being loaded, it calls that library's `native_init` entry point, providing it with a set of [API](include/core/native_api.h)s for creating its own native hooks.\n\n### `elf` - Symbol Resolution\n\nThis module is responsible for runtime symbol lookups in shared libraries, a critical function for native hooking.\n\n-   **`ElfImage`**: A parser for ELF files mapped into the current process's memory. It can resolve symbols in stripped binaries by locating, decompressing (using `xz-embedded`), and parsing the `.gnu_debugdata` section. It applies a cascading lookup strategy: GNU hash -> ELF hash -> linear scan of the symbol table.\n-   **`ElfSymbolCache`**: A thread-safe, lazy-initialized cache for `ElfImage` instances, providing a safe way to access common libraries like `libart.so` and the `linker`.\n\n### `jni` - The Business Logic Interface\n\nThis is the most significant module and represents the library's primary service layer. It contains a set of JNI bridges that expose the core features to the injected Java framework code. The functionality here is the main product of the native library.\n\n-   **`jni_bridge.h`**: Provides a set of helper macros (`VECTOR_NATIVE_METHOD`, `REGISTER_VECTOR_NATIVE_METHODS`, etc.) to standardize and simplify the tedious process of writing JNI boilerplate.\n-   **`HookBridge`**: The engine for ART method hooking. It maintains a thread-safe map of all active hooks. It also includes some stability controls, such as using atomic operations to set the backup method trampoline and throwing a Java exception instead of causing a native crash if a user tries to invoke the original method of a failed hook.\n-   **`ResourcesHook`**: Provides the functionality to intercept and rewrite Android's binary XML resources on the fly. It relies on non-public structures from `libandroidfw.so` and uses the `elf` module to find the necessary function symbols at runtime.\n-   **`DexParserBridge`**: Exposes a native DEX parser to the Java layer using a visitor pattern. This allows for analysis of an app's bytecode without the overhead of instantiating the entire DEX structure as Java objects.\n-   **`NativeApiBridge`**: The JNI counterpart to the `core/native_api`. It exposes a method for the Java framework to register the filenames of third-party native modules.\n\n### `common` & `framework`\n\n-   **`common`**: A collection of basic utilities, including a `fmt`-based logging system, global constants, and helper functions.\n-   **`framework`**: Contains minimal C++ structure definitions that mirror those inside Android's internal `libandroidfw.so`. These are necessary to correctly interpret resource data pointers.\n\n## 3. Build System\n\nThe library is configured with CMake to be built as a **static library (`libnative.a`)**. All external dependencies are also linked statically for maximum portability.\n"
  },
  {
    "path": "native/include/common/config.h",
    "content": "#pragma once\n\n/**\n * @file config.h\n * @brief Compile-time constants, version information, and platform-specific configurations.\n */\n\nnamespace vector::native {\n\n[[nodiscard]] constexpr bool IsDebugBuild() {\n#ifdef NDEBUG\n    return false;\n#else\n    return true;\n#endif\n}\n\n/// A compile-time constant indicating if this is a debug build.\ninline constexpr bool kIsDebugBuild = IsDebugBuild();\n\n/**\n * @def LP_SELECT(lp32, lp64)\n * @brief A preprocessor macro to select a value based on the architecture.\n * @param lp32 The value to use on 32-bit platforms.\n * @param lp64 The value to use on 64-bit platforms.\n */\n#if defined(__LP64__)\n#define LP_SELECT(lp32, lp64) lp64\n#else\n#define LP_SELECT(lp32, lp64) lp32\n#endif\n\n/// The filename of the core Android Runtime (ART) library.\ninline constexpr auto kArtLibraryName = \"libart.so\";\n\n/// The filename of the Android Binder library.\ninline constexpr auto kBinderLibraryName = \"libbinder.so\";\n\n/// The filename of the Android Framework library.\ninline constexpr auto kFrameworkLibraryName = \"libandroidfw.so\";\n\n/// The path to the dynamic linker.\ninline constexpr auto kLinkerPath = \"/linker\";\n\n/// The version code of the library, populated by the build system.\nconst int kVersionCode = VERSION_CODE;\n\n/// The version name of the library, populated by the build system.\nconst char *const kVersionName = VERSION_NAME;\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/include/common/logging.h",
    "content": "#pragma once\n\n#include <android/log.h>\n#include <fmt/format.h>\n\n#include <array>\n\n/**\n * @file logging.h\n * @brief Provides a lightweight logging framework using fmt.\n *\n */\n\n/// The tag used for all log messages from this library.\n#ifndef LOG_TAG\n#define LOG_TAG \"VectorNative\"\n#endif\n\n/**\n * @def LOGV(fmt, ...)\n * @brief Logs a verbose message. Compiled out in release builds.\n * Includes file, line, and function information.\n */\n\n/**\n * @def LOGD(fmt, ...)\n * @brief Logs a debug message. Compiled out in release builds.\n * Includes file, line, and function information.\n */\n\n/**\n * @def LOGI(fmt, ...)\n * @brief Logs an informational message.\n */\n\n/**\n * @def LOGW(fmt, ...)\n * @brief Logs a warning message.\n */\n\n/**\n * @def LOGE(fmt, ...)\n * @brief Logs an error message.\n */\n\n/**\n * @def LOGF(fmt, ...)\n * @brief Logs a fatal error message.\n */\n\n/**\n * @def PLOGE(fmt, ...)\n * @brief Logs an error message and appends the string representation of the\n * current `errno` value.\n */\n\n#ifdef LOG_DISABLED\n#define LOGV(...) ((void)0)\n#define LOGD(...) ((void)0)\n#define LOGI(...) ((void)0)\n#define LOGW(...) ((void)0)\n#define LOGE(...) ((void)0)\n#define LOGF(...) ((void)0)\n#define PLOGE(...) ((void)0)\n#else\n\nnamespace vector::native::detail {\ntemplate <typename... T>\ninline void LogToAndroid(int prio, const char *tag, fmt::format_string<T...> fmt, T &&...args) {\n    // Using a stack-allocated buffer for performance.\n    std::array<char, 1024> buf{};\n    // format_to_n is safe against buffer overflows.\n    auto result = fmt::format_to_n(buf.data(), buf.size() - 1, fmt, std::forward<T>(args)...);\n    buf[result.size] = '\\0';\n    __android_log_write(prio, tag, buf.data());\n}\n}  // namespace vector::native::detail\n\n#ifndef NDEBUG\n#define LOGV(fmt, ...)                                                                             \\\n    ::vector::native::detail::LogToAndroid(ANDROID_LOG_VERBOSE, LOG_TAG, \"{}:{} ({}): \" fmt,       \\\n                                           __FILE_NAME__, __LINE__,                                \\\n                                           __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)\n#define LOGD(fmt, ...)                                                                             \\\n    ::vector::native::detail::LogToAndroid(ANDROID_LOG_DEBUG, LOG_TAG, \"{}:{} ({}): \" fmt,         \\\n                                           __FILE_NAME__, __LINE__,                                \\\n                                           __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)\n#else\n#define LOGV(...) ((void)0)\n#define LOGD(...) ((void)0)\n#endif\n\n#define LOGI(fmt, ...)                                                                             \\\n    ::vector::native::detail::LogToAndroid(ANDROID_LOG_INFO, LOG_TAG,                              \\\n                                           fmt __VA_OPT__(, ) __VA_ARGS__)\n#define LOGW(fmt, ...)                                                                             \\\n    ::vector::native::detail::LogToAndroid(ANDROID_LOG_WARN, LOG_TAG,                              \\\n                                           fmt __VA_OPT__(, ) __VA_ARGS__)\n#define LOGE(fmt, ...)                                                                             \\\n    ::vector::native::detail::LogToAndroid(ANDROID_LOG_ERROR, LOG_TAG,                             \\\n                                           fmt __VA_OPT__(, ) __VA_ARGS__)\n#define LOGF(fmt, ...)                                                                             \\\n    ::vector::native::detail::LogToAndroid(ANDROID_LOG_FATAL, LOG_TAG,                             \\\n                                           fmt __VA_OPT__(, ) __VA_ARGS__)\n#define PLOGE(fmt, ...) LOGE(fmt \" failed with error {}: {}\", ##__VA_ARGS__, errno, strerror(errno))\n\n#endif  // LOG_DISABLED\n"
  },
  {
    "path": "native/include/core/config_bridge.h",
    "content": "#pragma once\n\n#include <map>\n#include <memory>\n#include <string>\n\n/**\n * @file config_bridge.h\n * @brief A native-side cache for configuration data, currently only the obfuscation map.\n */\n\nnamespace vector::native {\n\n/**\n * @class ConfigBridge\n * @brief A singleton that holds configuration data.\n */\nclass ConfigBridge {\npublic:\n    virtual ~ConfigBridge() = default;\n\n    ConfigBridge(const ConfigBridge &) = delete;\n    ConfigBridge &operator=(const ConfigBridge &) = delete;\n\n    /**\n     * @brief Gets the singleton instance of the ConfigBridge.\n     */\n    static ConfigBridge *GetInstance() { return instance_.get(); }\n\n    /**\n     * @brief Releases ownership of the singleton instance.\n     */\n    static std::unique_ptr<ConfigBridge> ReleaseInstance() { return std::move(instance_); }\n\n    /// Gets a reference to the obfuscation map.\n    virtual std::map<std::string, std::string> &obfuscation_map() = 0;\n\n    /// Sets the obfuscation map.\n    virtual void obfuscation_map(std::map<std::string, std::string> map) = 0;\n\nprotected:\n    ConfigBridge() = default;\n\n    /// The singleton instance, managed alongside the main Context.\n    static std::unique_ptr<ConfigBridge> instance_;\n};\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/include/core/context.h",
    "content": "#pragma once\n\n#include <sys/mman.h>\n\n#include <lsplant.hpp>\n#include <memory>\n#include <string_view>\n#include <utils/jni_helper.hpp>\n\n#include \"common/logging.h\"\n\n/**\n * @file context.h\n * @brief Defines the core runtime context for the Vector native environment.\n *\n * The Context class is a singleton that holds essential runtime information, such as\n * the injected class loader, and provides core functionalities like class finding and DEX loading.\n * It serves as the central hub for native operations.\n */\n\nnamespace vector::native {\n\n// Forward declaration from another module.\nclass ConfigBridge;\n\n/**\n * @class Context\n * @brief Manages the global state and core operations of the native library.\n *\n * This singleton is responsible for initializing hooks, managing DEX files,\n * and providing access to the application's class loader.\n * It orchestrates the setup process when the library is loaded into the target application.\n */\nclass Context {\npublic:\n    Context(const Context &) = delete;\n    Context &operator=(const Context &) = delete;\n\n    /**\n     * @brief Gets the singleton instance of the Context.\n     * @return A pointer to the global Context instance.\n     */\n    static Context *GetInstance();\n\n    /**\n     * @brief Releases ownership of the singleton instance.\n     *\n     * This is typically used during shutdown to clean up resources.\n     * After this call, GetInstance() will return nullptr until a new instance is created.\n     *\n     * @return A unique_ptr owning the Context instance.\n     */\n    static std::unique_ptr<Context> ReleaseInstance();\n\n    /**\n     * @brief Gets the class loader used for injecting framework classes.\n     * @return A global JNI reference to the class loader.\n     */\n    [[nodiscard]] jobject GetCurrentClassLoader() const { return inject_class_loader_; }\n\n    /**\n     * @brief Finds a class using the framework's injected class loader.\n     *\n     * This is the primary method for looking up classes that are part of the\n     * Vector framework's Java components.\n     *\n     * @param env The JNI environment.\n     * @param class_name The fully qualified name of the class to find (dot-separated).\n     * @return A ScopedLocalRef containing the jclass object, or nullptr if not found.\n     */\n    [[nodiscard]] lsplant::ScopedLocalRef<jclass> FindClassFromCurrentLoader(\n        JNIEnv *env, std::string_view class_name) const {\n        return FindClassFromLoader(env, GetCurrentClassLoader(), class_name);\n    }\n\n    virtual ~Context() = default;\n\nprotected:\n    /**\n     * @class PreloadedDex\n     * @brief Manages a memory-mapped DEX file.\n     *\n     * This helper class handles the mapping of a DEX file from a file descriptor\n     * into memory and ensures it is unmapped upon destruction.\n     */\n    class PreloadedDex {\n    public:\n        PreloadedDex() : addr_(nullptr), size_(0) {}\n        PreloadedDex(const PreloadedDex &) = delete;\n        PreloadedDex &operator=(const PreloadedDex &) = delete;\n\n        /**\n         * @brief Memory-maps a DEX file from a file descriptor.\n         * @param fd The file descriptor of the DEX file.\n         * @param size The size of the file.\n         */\n        PreloadedDex(int fd, size_t size);\n\n        PreloadedDex(PreloadedDex &&other) noexcept : addr_(other.addr_), size_(other.size_) {\n            other.addr_ = nullptr;\n            other.size_ = 0;\n        }\n\n        PreloadedDex &operator=(PreloadedDex &&other) noexcept {\n            if (this != &other) {\n                if (addr_) {\n                    munmap(addr_, size_);\n                }\n                addr_ = other.addr_;\n                size_ = other.size_;\n                other.addr_ = nullptr;\n                other.size_ = 0;\n            }\n            return *this;\n        }\n\n        ~PreloadedDex();\n\n        /// Checks if the DEX file was successfully mapped.\n        explicit operator bool() const { return addr_ != nullptr && size_ > 0; }\n        /// Returns the size of the mapped DEX data.\n        [[nodiscard]] auto size() const { return size_; }\n        /// Returns a pointer to the beginning of the mapped DEX data.\n        [[nodiscard]] auto data() const { return addr_; }\n\n    private:\n        void *addr_;\n        size_t size_;\n    };\n\n    Context() = default;\n\n    /**\n     * @brief Finds a class from a specific class loader instance.\n     * @param env The JNI environment.\n     * @param class_loader The class loader to use for the lookup.\n     * @param class_name The name of the class to find.\n     * @return A ScopedLocalRef containing the jclass, or nullptr if not found.\n     */\n    static lsplant::ScopedLocalRef<jclass> FindClassFromLoader(JNIEnv *env, jobject class_loader,\n                                                               std::string_view class_name);\n\n    /**\n     * @brief Finds and calls a static void method on the framework's entry class.\n     *\n     * A utility for internal communication between the native and Java layers.\n     *\n     * @tparam Args Argument types for the method call.\n     * @param env The JNI environment.\n     * @param method_name The name of the static method.\n     * @param method_sig The JNI signature of the method.\n     * @param args The arguments to pass to the method.\n     */\n    template <typename... Args>\n    void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig,\n                     Args &&...args) const {\n        if (!entry_class_) {\n            LOGE(\"Cannot call method '{}', entry class is null\", method_name.data());\n            return;\n        }\n        jmethodID mid = lsplant::JNI_GetStaticMethodID(env, entry_class_, method_name, method_sig);\n        if (mid) {\n            env->CallStaticVoidMethod(entry_class_, mid,\n                                      lsplant::UnwrapScope(std::forward<Args>(args))...);\n        } else {\n            LOGE(\"Static method '{}' with signature '{}' not found\", method_name.data(),\n                 method_sig.data());\n        }\n    }\n\n    // --- Virtual methods for platform-specific implementations ---\n\n    /// Initializes the ART hooking framework (LSPlant).\n    virtual void InitArtHooker(JNIEnv *env, const lsplant::InitInfo &initInfo);\n\n    /// Registers all necessary JNI bridges and native hooks.\n    virtual void InitHooks(JNIEnv *env);\n\n    /// Loads a DEX file into the target application.\n    virtual void LoadDex(JNIEnv *env, PreloadedDex &&dex) = 0;\n\n    /// Sets up the main entry class for native-to-Java calls.\n    virtual void SetupEntryClass(JNIEnv *env) = 0;\n\nprotected:\n    /// The singleton instance of the Context.\n    static std::unique_ptr<Context> instance_;\n\n    /// Global reference to the classloader used to load the framework.\n    jobject inject_class_loader_ = nullptr;\n\n    /// Global reference to the primary entry point class in the Java framework.\n    jclass entry_class_ = nullptr;\n};\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/include/core/native_api.h",
    "content": "#pragma once\n\n#include <dlfcn.h>\n#include <dobby.h>\n\n#include <string>\n#include <utils/hook_helper.hpp>\n\n#include \"common/config.h\"\n#include \"common/logging.h\"\n\n/**\n * @file native_api.h\n * @brief Manages the native module ecosystem and provides a stable API for them.\n *\n * This component is responsible for hooking the dynamic library loader (`dlopen`) to\n * detect when registered native modules are loaded.\n * It then provides these modules with a set of function pointers for\n * interacting with the Vector core, primarily for creating native hooks.\n */\n\n// NOTE: The following type definitions form a public ABI for native modules.\n// Do not change them without careful consideration for backward compatibility.\n\n/*\n * =========================================================================================\n *  Vector Native API Interface\n * =========================================================================================\n *\n * This following function types and data structures allow a native library (your module) to\n * interface with the Vector framework.\n * The core idea is that Vector provides a set of powerful tools (like function hooking),\n * and your module consumes these tools through a well-defined entry point.\n *\n * The interaction flow is as follows:\n *\n *   1. Vector intercepts the loading of your native library (e.g., libnative.so).\n *   2. Vector looks for and calls the `native_init` function within your library.\n *   3. Vector passes a `NativeAPIEntries` struct to your `native_init`,\n *      which contains function pointers to Vector's hooking\n *      and unhooking implementations (powered by Dobby).\n *   4. Your `native_init` function saves these function pointers for later use\n *      and returns a callback function (`NativeOnModuleLoaded`).\n *   5. Vector will then invoke your returned callback every time\n *      a new native library is loaded into the target process,\n *      allowing you to perform \"late\" hooks on specific libraries.\n *\n *\n * Initialization Flow\n *\n *   Vector Framework                    Your Native Module (e.g., libnative.so)\n *   -----------------                    -------------------------------------\n *\n *        |                                            |\n * [ Intercepts dlopen(\"libnative.so\") ]               |\n *        |                                            |\n *        |----------> [ Finds & Calls native_init() ] |\n *        |                                            |\n *   [ Passes NativeAPIEntries* ]  ---> [ Stores function pointers ]\n *   (Contains hook/unhook funcs)                      |\n *        |                                            |\n *        |                                            |\n *        |             <-----------[ Returns `NativeOnModuleLoaded` callback ]\n *        |                                            |\n *        |                                            |\n *   [ Stores your callback ]                          |\n *        |                                            |\n *\n */\n\n// Function pointer type for a native hooking implementation.\nusing HookFunType = int (*)(void *func, void *replace, void **backup);\n\n// Function pointer type for a native unhooking implementation.\nusing UnhookFunType = int (*)(void *func);\n\n// Callback function pointer that modules receive, invoked when any library is loaded.\nusing NativeOnModuleLoaded = void (*)(const char *name, void *handle);\n\n/**\n * @struct NativeAPIEntries\n * @brief A struct containing function pointers exposed to native modules.\n */\nstruct NativeAPIEntries {\n    uint32_t version;          // The version of this API struct.\n    HookFunType hookFunc;      // Pointer to the function for inline  hooking.\n    UnhookFunType unhookFunc;  // Pointer to the function for unhooking.\n};\n\n// NOTE: Module developers should not include the following INTERNAL definitions.\n\nnamespace vector::native {\n\n// The entry point function that native modules must export (`native_init`).\nusing NativeInit = NativeOnModuleLoaded (*)(const NativeAPIEntries *entries);\n\n/**\n * @brief Installs the hooks required for the native API to function.\n * @param handler The LSPlant hook handler.\n * @return True on success, false on failure.\n */\nbool InstallNativeAPI(const lsplant::HookHandler &handler);\n\n/**\n * @brief Registers a native library by its filename for module initialization.\n *\n * When a library with a matching filename is loaded via `dlopen`, the runtime will attempt to\n * initialize it as a native module by calling its `native_init` function.\n *\n * @param library_name The filename of the native module's .so file (e.g., \"libmymodule.so\").\n */\nvoid RegisterNativeLib(const std::string &library_name);\n\n/**\n * @brief A wrapper around DobbyHook.\n */\ninline int HookInline(void *original, void *replace, void **backup) {\n    if constexpr (kIsDebugBuild) {\n        Dl_info info;\n        if (dladdr(original, &info)) {\n            LOGD(\"Dobby hooking {} ({}) from {} ({})\",\n                 info.dli_sname ? info.dli_sname : \"(unknown symbol)\",\n                 info.dli_saddr ? info.dli_saddr : original,\n                 info.dli_fname ? info.dli_fname : \"(unknown file)\", info.dli_fbase);\n        }\n    }\n    return DobbyHook(original, reinterpret_cast<dobby_dummy_func_t>(replace),\n                     reinterpret_cast<dobby_dummy_func_t *>(backup));\n}\n\n/**\n * @brief A wrapper around DobbyDestroy.\n */\ninline int UnhookInline(void *original) {\n    if constexpr (kIsDebugBuild) {\n        Dl_info info;\n        if (dladdr(original, &info)) {\n            LOGD(\"Dobby unhooking {} ({}) from {} ({})\",\n                 info.dli_sname ? info.dli_sname : \"(unknown symbol)\",\n                 info.dli_saddr ? info.dli_saddr : original,\n                 info.dli_fname ? info.dli_fname : \"(unknown file)\", info.dli_fbase);\n        }\n    }\n    return DobbyDestroy(original);\n}\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/include/elf/elf_image.h",
    "content": "#pragma once\n\n#include <link.h>\n#include <linux/elf.h>\n\n#include <map>\n#include <string>\n#include <string_view>\n#include <vector>\n\n/**\n * @file elf_image.h\n * @brief Defines the ElfImage class for parsing ELF files from memory.\n *\n * This utility can find the base address of a loaded shared library, parse its ELF headers, and\n * look up symbol addresses using various methods (GNU hash, ELF hash, and linear search).\n *\n * It handles stripped ELF files by decompressing and parsing the `.gnu_debugdata` section.\n */\n\nnamespace vector::native {\n\n/**\n * @class ElfImage\n * @brief Represents a loaded ELF shared library in the current process.\n *\n * An ElfImage instance is created with the filename of a library (e.g., \"libart.so\").\n * It automatically finds the library's base address in memory by parsing `/proc/self/maps` and\n * then memory-maps the ELF file from disk to parse its headers.\n */\nclass ElfImage {\npublic:\n    /**\n     * @brief Constructs an ElfImage for a given shared library.\n     * @param lib_name The filename of the library (e.g., \"libart.so\", \"/linker\").\n     */\n    explicit ElfImage(std::string_view lib_name);\n    ~ElfImage();\n\n    // Disable copy and assignment to prevent accidental slicing and resource mismanagement.\n    ElfImage(const ElfImage &) = delete;\n    ElfImage &operator=(const ElfImage &) = delete;\n\n    /**\n     * @brief Finds the memory address of a symbol by its name.\n     *\n     * This method attempts to resolve a symbol's address using, in order:\n     * 1. The GNU hash table (.gnu.hash) for fast lookups.\n     * 2. The standard ELF hash table (.hash) as a fallback.\n     * 3. A linear search through the full symbol table (.symtab).\n     *\n     * @tparam T The desired pointer type (e.g., `void*`, `int (*)(...)`).\n     * @param name The name of the symbol to find.\n     * @return The absolute memory address of the symbol, or nullptr if not found.\n     */\n    template <typename T = void *>\n        requires(std::is_pointer_v<T>)\n    const T getSymbAddress(std::string_view name) const {\n        // Pre-calculate hashes for efficiency.\n        auto gnu_hash = GnuHash(name);\n        auto elf_hash = ElfHash(name);\n        auto offset = getSymbOffset(name, gnu_hash, elf_hash);\n        if (offset > 0 && base_ != nullptr) {\n            // The final address is: base_address + symbol_offset - load_bias\n            return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(base_) + offset - bias_);\n        }\n        return nullptr;\n    }\n\n    /**\n     * @brief Finds the first symbol whose name starts with the given prefix.\n     *\n     * This is useful for finding symbols when the exact name is unknown, such as mangled C++\n     * symbols. This search is performed only on the full symbol table (.symtab) and may be slow.\n     *\n     * @tparam T The desired pointer type.\n     * @param prefix The prefix to search for.\n     * @return The address of the first matching symbol, or nullptr if none is found.\n     */\n    template <typename T = void *>\n        requires(std::is_pointer_v<T>)\n    const T getSymbPrefixFirstAddress(std::string_view prefix) const {\n        auto offset = prefixLookupFirst(prefix);\n        if (offset > 0 && base_ != nullptr) {\n            return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(base_) + offset - bias_);\n        }\n        return nullptr;\n    }\n\n    /**\n     * @brief Checks if the ELF image was successfully loaded and parsed.\n     * @return True if the image is valid, false otherwise.\n     */\n    [[nodiscard]] bool IsValid() const { return base_ != nullptr; }\n\n    /**\n     * @brief Returns the canonical path of the loaded library, as found in /proc/self/maps.\n     */\n    [[nodiscard]] const std::string &GetPath() const { return path_; }\n\nprivate:\n    // Finds the base address of the library in the current process's memory map.\n    bool findModuleBase();\n    // Parses the main ELF headers from a given header pointer.\n    void parseHeaders(ElfW(Ehdr) * header);\n    // Decompresses the .gnu_debugdata section if it exists.\n    bool decompressGnuDebugData();\n\n    // Looks up a symbol offset using the ELF hash table.\n    ElfW(Addr) elfLookup(std::string_view name, uint32_t hash) const;\n    // Looks up a symbol offset using the GNU hash table.\n    ElfW(Addr) gnuLookup(std::string_view name, uint32_t hash) const;\n    // Looks up a symbol offset via a linear scan of the .symtab section.\n    ElfW(Addr) linearLookup(std::string_view name) const;\n    // Finds all symbol offsets with a given name via a linear scan.\n    std::vector<ElfW(Addr)> linearRangeLookup(std::string_view name) const;\n    // Finds the first symbol offset whose name starts with the given prefix.\n    ElfW(Addr) prefixLookupFirst(std::string_view prefix) const;\n\n    // Gets a symbol's offset from the start of the file.\n    ElfW(Addr) getSymbOffset(std::string_view name, uint32_t gnu_hash, uint32_t elf_hash) const;\n\n    // Lazily initializes the map for linear symbol lookups from the .symtab section.\n    void ensureLinearMapInitialized() const;\n\n    // Calculates the standard ELF hash for a symbol name.\n    [[nodiscard]] static constexpr uint32_t ElfHash(std::string_view name);\n    // Calculates the GNU hash for a symbol name.\n    [[nodiscard]] static constexpr uint32_t GnuHash(std::string_view name);\n\n    // --- Member Variables ---\n\n    std::string path_;\n    void *base_ = nullptr;\n    void *file_map_ = nullptr;\n    size_t file_size_ = 0;\n    ElfW(Addr) bias_ = 0;\n    bool bias_calculated_ = false;\n\n    // Pointers into the mapped ELF file data.\n    ElfW(Ehdr) *header_ = nullptr;\n    ElfW(Shdr) *dynsym_ = nullptr;\n    ElfW(Sym) *dynsym_start_ = nullptr;\n    const char *strtab_start_ = nullptr;  // Note: const char* is safer.\n\n    // ELF hash section fields\n    uint32_t nbucket_ = 0;\n    uint32_t *bucket_ = nullptr;\n    uint32_t *chain_ = nullptr;\n\n    // GNU hash section fields\n    uint32_t gnu_nbucket_ = 0;\n    uint32_t gnu_symndx_ = 0;\n    uint32_t gnu_bloom_size_ = 0;\n    uint32_t gnu_shift2_ = 0;\n    uintptr_t *gnu_bloom_filter_ = nullptr;\n    uint32_t *gnu_bucket_ = nullptr;\n    uint32_t *gnu_chain_ = nullptr;\n\n    // For stripped binaries with .gnu_debugdata\n    std::string elf_debugdata_;\n    ElfW(Ehdr) *header_debugdata_ = nullptr;\n    ElfW(Sym) *symtab_start_ = nullptr;\n    ElfW(Off) symtab_count_ = 0;\n    const char *symtab_str_start_ = nullptr;\n\n    // Lazily-initialized map for fast linear lookups.\n    // `mutable` allows init in const methods.\n    mutable std::map<std::string_view, ElfW(Sym) *> symtabs_;\n};\n\n// --- Inlined Hash Function Implementations ---\n\nconstexpr uint32_t ElfImage::ElfHash(std::string_view name) {\n    uint32_t h = 0, g;\n    for (unsigned char p : name) {\n        h = (h << 4) + p;\n        if ((g = h & 0xf0000000) != 0) {\n            h ^= g >> 24;\n        }\n        h &= ~g;\n    }\n    return h;\n}\n\nconstexpr uint32_t ElfImage::GnuHash(std::string_view name) {\n    uint32_t h = 5381;\n    for (unsigned char p : name) {\n        h = (h << 5) + h + p;  // h * 33 + p\n    }\n    return h;\n}\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/include/elf/symbol_cache.h",
    "content": "#pragma once\n\n/**\n * @file symbol_cache.h\n * @brief Provides a thread-safe, lazy-initialized cache for commonly used ElfImage objects.\n *\n * This avoids the cost of repeatedly parsing the ELF files for libart, libbinder,\n * and the linker during runtime.\n */\n\nnamespace vector::native {\n\n// Forward declaration\nclass ElfImage;\n\n/**\n * @class ElfSymbolCache\n * @brief A singleton cache for frequently accessed system library ELF images.\n *\n * All methods are static and guarantee thread-safe, one-time initialization\n * of the underlying ElfImage objects.\n */\nclass ElfSymbolCache {\npublic:\n    /**\n     * @brief Gets the cached ElfImage for the ART library (libart.so).\n     * @return A const pointer to the ElfImage, or nullptr if it could not be loaded.\n     */\n    static const ElfImage *GetArt();\n\n    /**\n     * @brief Gets the cached ElfImage for the Binder library (libbinder.so).\n     * @return A const pointer to the ElfImage, or nullptr if it could not be loaded.\n     */\n    static const ElfImage *GetLibBinder();\n\n    /**\n     * @brief Gets the cached ElfImage for the dynamic linker.\n     * @return A const pointer to the ElfImage, or nullptr if it could not be loaded.\n     */\n    static const ElfImage *GetLinker();\n\n    /**\n     * @brief Clears the cache for a specific ElfImage object.\n     *\n     * If the provided pointer matches one of the cached images, that specific cache entry will be cleared,\n     * forcing a reload on the next `Get...()` call for that library.\n     * If the pointer does not match any cached image, this function does nothing.\n     *\n     * @param image_to_clear A pointer to the cached ElfImage to be removed.\n     */\n    static bool ClearCache(const ElfImage *image_to_clear);\n\n    /**\n     * @brief Clears the cache, releasing all ElfImage objects.\n     *\n     * This is primarily for testing or specific shutdown scenarios.\n     * After this call, the next call to a Get...() method will reload the library from scratch.\n     */\n    static void ClearCache();\n};\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/include/framework/android_types.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <variant>\n\n#include \"utils/hook_helper.hpp\"\n\nusing lsplant::operator\"\"_sym;\n\n// References:\n// https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h\nnamespace android {\n\ntypedef int32_t status_t;\n\ntemplate <class E>\nstruct unexpected {\n    E val_;\n};\n\ntemplate <class T, class E>\nstruct expected {\n    using value_type = T;\n    using error_type = E;\n    using unexpected_type = unexpected<E>;\n    std::variant<value_type, unexpected_type> var_;\n\n    constexpr bool has_value() const noexcept { return var_.index() == 0; }\n\n    constexpr const T &value() const & { return std::get<T>(var_); }\n\n    constexpr T &value() & { return std::get<T>(var_); }\n\n    constexpr const T *operator->() const { return std::addressof(value()); }\n\n    constexpr T *operator->() { return std::addressof(value()); }\n};\n\nenum class IOError {\n    // Used when reading a file residing on an IncFs file-system times out.\n    PAGES_MISSING = -1,\n};\n\ntemplate <typename TChar>\nstruct BasicStringPiece {\n    const TChar *data_;\n    size_t length_;\n};\n\nusing NullOrIOError = std::variant<std::nullopt_t, IOError>;\n\nusing StringPiece16 = BasicStringPiece<char16_t>;\n\nenum {\n    RES_NULL_TYPE = 0x0000,\n    RES_STRING_POOL_TYPE = 0x0001,\n    RES_TABLE_TYPE = 0x0002,\n    RES_XML_TYPE = 0x0003,\n    // Chunk types in RES_XML_TYPE\n    RES_XML_FIRST_CHUNK_TYPE = 0x0100,\n    RES_XML_START_NAMESPACE_TYPE = 0x0100,\n    RES_XML_END_NAMESPACE_TYPE = 0x0101,\n    RES_XML_START_ELEMENT_TYPE = 0x0102,\n    RES_XML_END_ELEMENT_TYPE = 0x0103,\n    RES_XML_CDATA_TYPE = 0x0104,\n    RES_XML_LAST_CHUNK_TYPE = 0x017f,\n    // This contains a uint32_t array mapping strings in the string\n    // pool back to resource identifiers.  It is optional.\n    RES_XML_RESOURCE_MAP_TYPE = 0x0180,\n    // Chunk types in RES_TABLE_TYPE\n    RES_TABLE_PACKAGE_TYPE = 0x0200,\n    RES_TABLE_TYPE_TYPE = 0x0201,\n    RES_TABLE_TYPE_SPEC_TYPE = 0x0202,\n    RES_TABLE_LIBRARY_TYPE = 0x0203\n};\n\nstruct ResXMLTree_node {\n    void *header;\n    // Line number in original source file at which this element appeared.\n    uint32_t lineNumber;\n    // Optional XML comment that was associated with this element; -1 if none.\n    void *comment;\n};\n\nclass ResXMLTree;\n\nclass ResXMLParser {\npublic:\n    enum event_code_t {\n        BAD_DOCUMENT = -1,\n        START_DOCUMENT = 0,\n        END_DOCUMENT = 1,\n\n        FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE,\n\n        START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE,\n        END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE,\n        START_TAG = RES_XML_START_ELEMENT_TYPE,\n        END_TAG = RES_XML_END_ELEMENT_TYPE,\n        TEXT = RES_XML_CDATA_TYPE\n    };\n\n    const ResXMLTree &mTree;\n    event_code_t mEventCode;\n    const ResXMLTree_node *mCurNode;\n    const void *mCurExt;\n};\n\nclass ResStringPool {\npublic:\n    status_t mError;\n    void *mOwnedData;\n    const void *mHeader;\n    size_t mSize;\n    mutable pthread_mutex_t mDecodeLock;\n    const uint32_t *mEntries;\n    const uint32_t *mEntryStyles;\n    const void *mStrings;\n    char16_t mutable **mCache;\n    uint32_t mStringPoolSize;  // number of uint16_t\n    const uint32_t *mStyles;\n    uint32_t mStylePoolSize;  // number of uint32_t\n\n    using stringAtRet = expected<StringPiece16, NullOrIOError>;\n\n    inline static auto stringAtS_ = (\"_ZNK7android13ResStringPool8stringAtEjPj\"_sym |\n                                     \"_ZNK7android13ResStringPool8stringAtEmPm\"_sym)\n                                        .as<stringAtRet (ResStringPool::*)(size_t)>;\n\n    inline static auto stringAt_ = (\"_ZNK7android13ResStringPool8stringAtEj\"_sym |\n                                    \"_ZNK7android13ResStringPool8stringAtEm\"_sym)\n                                       .as<const char16_t *(ResStringPool::*)(size_t, size_t *)>;\n\n    StringPiece16 stringAt(size_t idx) const {\n        if (stringAt_) {\n            size_t len;\n            const char16_t *str = stringAt_(const_cast<ResStringPool *>(this), idx, &len);\n            return {str, len};\n        } else if (stringAtS_) {\n            auto str = stringAtS_(const_cast<ResStringPool *>(this), idx);\n            if (str.has_value()) {\n                return {str->data_, str->length_};\n            }\n        }\n        return {nullptr, 0u};\n    }\n\n    static bool setup(const lsplant::HookHandler &handler) {\n        return handler(stringAt_) || handler(stringAtS_);\n    }\n};\n\nclass ResXMLTree : public ResXMLParser {\npublic:\n    void *mDynamicRefTable;\n    status_t mError;\n    void *mOwnedData;\n    const void *mHeader;\n    size_t mSize;\n    const uint8_t *mDataEnd;\n    ResStringPool mStrings;\n    const uint32_t *mResIds;\n    size_t mNumResIds;\n    const ResXMLTree_node *mRootNode;\n    const void *mRootExt;\n    event_code_t mRootCode;\n};\n\nstruct ResStringPool_ref {\n    // Index into the string pool table at which\n    // to find the location of the string data in the pool.\n    // (uint32_t-offset from the indices immediately after ResStringPool_header)\n    uint32_t index;\n};\n\nstruct ResXMLTree_attrExt {\n    // String of the full namespace of this element.\n    struct ResStringPool_ref ns;\n\n    // String name of this node if it is an ELEMENT; the raw character data if this is a CDATA node.\n    struct ResStringPool_ref name;\n\n    // Byte offset from the start of this structure where the attributes start.\n    uint16_t attributeStart;\n\n    // Size of the ResXMLTree_attribute structures that follow.\n    uint16_t attributeSize;\n\n    // Number of attributes associated with an ELEMENT.\n    // These are available as an array of ResXMLTree_attribute structures\n    // immediately following this node.\n    uint16_t attributeCount;\n\n    // Index (1-based) of the \"id\" attribute. 0 if none.\n    uint16_t idIndex;\n\n    // Index (1-based) of the \"class\" attribute. 0 if none.\n    uint16_t classIndex;\n\n    // Index (1-based) of the \"style\" attribute. 0 if none.\n    uint16_t styleIndex;\n};\n\nstruct Res_value {\n    // Number of bytes in this structure.\n    uint16_t size;\n    // Always set to 0.\n    uint8_t res0;\n\n    // Type of the data value.\n    enum : uint8_t {\n        // The 'data' is either 0 or 1, specifying this resource is\n        // either undefined or empty, respectively.\n        TYPE_NULL = 0x00,\n        // The 'data' holds a ResTable_ref, a reference to another resource table entry.\n        TYPE_REFERENCE = 0x01,\n        // The 'data' holds an attribute resource identifier.\n        TYPE_ATTRIBUTE = 0x02,\n        // The 'data' holds an index into the containing resource table's global value string pool.\n        TYPE_STRING = 0x03,\n        // The 'data' holds a single-precision floating point number.\n        TYPE_FLOAT = 0x04,\n        // The 'data' holds a complex number encoding a dimension value, such as \"100in\".\n        TYPE_DIMENSION = 0x05,\n        // The 'data' holds a complex number encoding a fraction of a container.\n        TYPE_FRACTION = 0x06,\n        // The 'data' holds a dynamic ResTable_ref,\n        // which needs to be resolved before it can be used like a TYPE_REFERENCE.\n        TYPE_DYNAMIC_REFERENCE = 0x07,\n        // The 'data' holds an attribute resource identifier,\n        // which needs to be resolved before it can be used like a TYPE_ATTRIBUTE.\n        TYPE_DYNAMIC_ATTRIBUTE = 0x08,\n        // Beginning of integer flavors...\n        TYPE_FIRST_INT = 0x10,\n        // The 'data' is a raw integer value of the form n..n.\n        TYPE_INT_DEC = 0x10,\n        // The 'data' is a raw integer value of the form 0xn..n.\n        TYPE_INT_HEX = 0x11,\n        // The 'data' is either 0 or 1, for input \"false\" or \"true\" respectively.\n        TYPE_INT_BOOLEAN = 0x12,\n        // Beginning of color integer flavors...\n        TYPE_FIRST_COLOR_INT = 0x1c,\n        // The 'data' is a raw integer value of the form #aarrggbb.\n        TYPE_INT_COLOR_ARGB8 = 0x1c,\n        // The 'data' is a raw integer value of the form #rrggbb.\n        TYPE_INT_COLOR_RGB8 = 0x1d,\n        // The 'data' is a raw integer value of the form #argb.\n        TYPE_INT_COLOR_ARGB4 = 0x1e,\n        // The 'data' is a raw integer value of the form #rgb.\n        TYPE_INT_COLOR_RGB4 = 0x1f,\n        // ...end of integer flavors.\n        TYPE_LAST_COLOR_INT = 0x1f,\n        // ...end of integer flavors.\n        TYPE_LAST_INT = 0x1f\n    };\n    uint8_t dataType;\n    // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION)\n    enum {\n        // Where the unit type information is.\n        // This gives us 16 possible types, as defined below.\n        COMPLEX_UNIT_SHIFT = 0,\n        COMPLEX_UNIT_MASK = 0xf,\n        // TYPE_DIMENSION: Value is raw pixels.\n        COMPLEX_UNIT_PX = 0,\n        // TYPE_DIMENSION: Value is Device Independent Pixels.\n        COMPLEX_UNIT_DIP = 1,\n        // TYPE_DIMENSION: Value is a Scaled device independent Pixels.\n        COMPLEX_UNIT_SP = 2,\n        // TYPE_DIMENSION: Value is in points.\n        COMPLEX_UNIT_PT = 3,\n        // TYPE_DIMENSION: Value is in inches.\n        COMPLEX_UNIT_IN = 4,\n        // TYPE_DIMENSION: Value is in millimeters.\n        COMPLEX_UNIT_MM = 5,\n        // TYPE_FRACTION: A basic fraction of the overall size.\n        COMPLEX_UNIT_FRACTION = 0,\n        // TYPE_FRACTION: A fraction of the parent size.\n        COMPLEX_UNIT_FRACTION_PARENT = 1,\n\n        // Where the radix information is, telling where the decimal place appears in the mantissa.\n        // This give us 4 possible fixed point representations as defined below.\n        COMPLEX_RADIX_SHIFT = 4,\n        COMPLEX_RADIX_MASK = 0x3,\n        // The mantissa is an integral number -- i.e., 0xnnnnnn.0\n        COMPLEX_RADIX_23p0 = 0,\n        // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn\n        COMPLEX_RADIX_16p7 = 1,\n        // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn\n        COMPLEX_RADIX_8p15 = 2,\n        // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn\n        COMPLEX_RADIX_0p23 = 3,\n\n        // Where the actual value is.\n        // This gives us 23 bits of precision.\n        // The top bit is the sign.\n        COMPLEX_MANTISSA_SHIFT = 8,\n        COMPLEX_MANTISSA_MASK = 0xffffff\n    };\n    // Possible data values for TYPE_NULL.\n    enum {\n        // The value is not defined.\n        DATA_NULL_UNDEFINED = 0,\n        // The value is explicitly defined as empty.\n        DATA_NULL_EMPTY = 1\n    };\n    // The data for this item, as interpreted according to dataType.\n    typedef uint32_t data_type;\n    data_type data;\n};\n\nstruct ResXMLTree_attribute {\n    // Namespace of this attribute.\n    struct ResStringPool_ref ns;\n\n    // Name of this attribute.\n    struct ResStringPool_ref name;\n\n    // The original raw string value of this attribute.\n    struct ResStringPool_ref rawValue;\n\n    // Processesd typed value of this attribute.\n    struct Res_value typedValue;\n};\n\n}  // namespace android\n"
  },
  {
    "path": "native/include/jni/jni_bridge.h",
    "content": "#pragma once\n\n#include <string>\n\n#include \"common/logging.h\"\n#include \"core/config_bridge.h\"\n#include \"core/context.h\"\n\n/**\n * @file jni_bridge.h\n * @brief Provides essential macros and helper functions for creating JNI bridges.\n *\n */\n\nnamespace vector::native::jni {\n\n/**\n * @brief Returns the number of elements in a statically-allocated C-style array.\n *\n * This is a compile-time constant.\n * Attempting to use this on a pointer will result in a compilation error,\n * preventing common mistakes.\n *\n * @tparam T The type of the array elements.\n * @tparam N The size of the array.\n * @param arr A reference to the array.\n * @return The number of elements in the array.\n */\ntemplate <typename T, size_t N>\n[[nodiscard]] constexpr inline size_t ArraySize(T (&)[N]) {\n    return N;\n}\n\n/**\n * @brief A helper function to get the obfuscated native bridge class signature prefix.\n *\n * It reads the obfuscation map to find the correct, potentially obfuscated,\n * package name for the native bridge classes.\n *\n * @return The JNI signature prefix (e.g., \"org/matrix/vector/nativebridge/\").\n */\ninline std::string GetNativeBridgeSignature() {\n    auto *bridge = ConfigBridge::GetInstance();\n    if (bridge) {\n        const auto &obfs_map = bridge->obfuscation_map();\n        // The key must match what the Java build script places in the map.\n        auto it = obfs_map.find(\"org.matrix.vector.nativebridge.\");\n        if (it != obfs_map.end()) {\n            return it->second;\n        }\n    }\n    // Fallback or default value if not found.\n    return \"org/matrix/vector/nativebridge/\";\n}\n\n/**\n * @brief Internal implementation for registering native methods.\n *\n * Finds the target class using the framework's class loader and calls JNI's RegisterNatives.\n */\n[[gnu::always_inline]]\ninline bool RegisterNativeMethodsInternal(JNIEnv *env, std::string_view class_name,\n                                          const JNINativeMethod *methods, jint method_count) {\n    auto *context = Context::GetInstance();\n    if (!context) {\n        LOGF(\"Cannot register natives for '{}', Context is null.\", class_name.data());\n        return false;\n    }\n    auto clazz = context->FindClassFromCurrentLoader(env, class_name);\n    if (clazz.get() == nullptr) {\n        LOGF(\"JNI class not found: {}\", class_name.data());\n        return false;\n    }\n    return env->RegisterNatives(clazz.get(), methods, method_count) == JNI_OK;\n}\n\n// A helper cast for the native method function pointers.\n#define VECTOR_JNI_CAST(to) reinterpret_cast<to>\n\n/**\n * @def VECTOR_NATIVE_METHOD(className, functionName, signature)\n * @brief Defines a JNINativeMethod entry.\n *\n * This macro constructs a JNINativeMethod struct, automatically\n * creating the mangled C-style function name that JNI expects.\n *\n * @param className The simple name of the Java class (e.g., \"HookBridge\").\n * @param functionName The name of the Java method (e.g., \"hookMethod\").\n * @param signature The JNI signature of the method (e.g., \"(I)V\").\n */\n#define VECTOR_NATIVE_METHOD(className, functionName, signature)                                   \\\n    {#functionName, signature,                                                                     \\\n     VECTOR_JNI_CAST(void *)(Java_org_matrix_vector_nativebridge_##className##_##functionName)}\n\n/**\n * @def JNI_START\n * @brief Defines the standard first two arguments for any JNI native method implementation.\n */\n#define JNI_START [[maybe_unused]] JNIEnv *env, [[maybe_unused]] jclass clazz\n\n/**\n * @def VECTOR_DEF_NATIVE_METHOD(ret, className, functionName, ...)\n * @brief Defines the function signature for a JNI native method implementation.\n *\n * This macro creates the full C++ function definition with\n * the correct JNI name-mangling convention.\n */\n#define VECTOR_DEF_NATIVE_METHOD(ret, className, functionName, ...)                                \\\n    extern \"C\" JNIEXPORT ret JNICALL                                                               \\\n        Java_org_matrix_vector_nativebridge_##className##_##functionName(JNI_START, ##__VA_ARGS__)\n\n/**\n * @def REGISTER_VECTOR_NATIVE_METHODS(class_name)\n * @brief Registers all methods defined in the `gMethods` array for a given class.\n *\n * This is the final step in linking the C++ implementations to the Java native methods.\n */\n#define REGISTER_VECTOR_NATIVE_METHODS(class_name)                                                 \\\n    RegisterNativeMethodsInternal(env, GetNativeBridgeSignature() + #class_name, gMethods,         \\\n                                  ArraySize(gMethods))\n\n}  // namespace vector::native::jni\n"
  },
  {
    "path": "native/include/jni/jni_hooks.h",
    "content": "#pragma once\n\n#include <jni.h>\n\n/**\n * @file jni_hooks.h\n * @brief Declares the registration functions for all JNI bridge modules.\n */\n\nnamespace vector::native::jni {\n\n/// Registers the JNI methods for the DexParserBridge.\nvoid RegisterDexParserBridge(JNIEnv *env);\n\n/// Registers the JNI methods for the HookBridge.\nvoid RegisterHookBridge(JNIEnv *env);\n\n/// Registers the JNI methods for the NativeApiBridge.\nvoid RegisterNativeApiBridge(JNIEnv *env);\n\n/// Registers the JNI methods for the ResourcesHook bridge.\nvoid RegisterResourcesHook(JNIEnv *env);\n\n}  // namespace vector::native::jni\n"
  },
  {
    "path": "native/src/core/context.cpp",
    "content": "#include \"core/context.h\"\n\n#include \"core/config_bridge.h\"\n#include \"jni/jni_hooks.h\"\n\nnamespace vector::native {\n\n// Instantiate the singleton pointers for Context and ConfigBridge.\nstd::unique_ptr<Context> Context::instance_;\nstd::unique_ptr<ConfigBridge> ConfigBridge::instance_;\n\nContext *Context::GetInstance() { return instance_.get(); }\n\nstd::unique_ptr<Context> Context::ReleaseInstance() { return std::move(instance_); }\n\nContext::PreloadedDex::PreloadedDex(int fd, size_t size) {\n    LOGD(\"Mapping PreloadedDex: fd={}, size={}\", fd, size);\n    void *addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);\n\n    if (addr != MAP_FAILED) {\n        addr_ = addr;\n        size_ = size;\n    } else {\n        addr_ = nullptr;\n        size_ = 0;\n        PLOGE(\"Failed to mmap dex file\");\n    }\n}\n\nContext::PreloadedDex::~PreloadedDex() {\n    if (addr_ && size_ > 0) {\n        munmap(addr_, size_);\n    }\n}\n\nvoid Context::InitArtHooker(JNIEnv *env, const lsplant::InitInfo &initInfo) {\n    if (!lsplant::Init(env, initInfo)) {\n        LOGE(\"Failed to initialize LSPlant hooking framework.\");\n    }\n}\n\nvoid Context::InitHooks(JNIEnv *env) {\n    // -------------------------------------------------------------------------\n    // DEX Privilege Elevation\n    // -------------------------------------------------------------------------\n    // We traverse the DexPathList of the injected ClassLoader to access the\n    // underlying 'mCookie' for every loaded DEX file.\n    // The cookie provides a handle to the native C++ DexFile object in ART memory.\n\n    // Retrieve the DexPathList object (holds the array of DEX elements).\n    auto path_list = lsplant::JNI_GetObjectFieldOf(env, inject_class_loader_, \"pathList\",\n                                                   \"Ldalvik/system/DexPathList;\");\n    if (!path_list) {\n        LOGE(\"InitHooks: Failed to retrieve 'pathList' from the injected class loader.\");\n        return;\n    }\n\n    // Retrieve the 'dexElements' array, which contains the actual DEX files and resources.\n    auto elements = lsplant::JNI_Cast<jobjectArray>(lsplant::JNI_GetObjectFieldOf(\n        env, path_list, \"dexElements\", \"[Ldalvik/system/DexPathList$Element;\"));\n    if (!elements) {\n        LOGE(\"InitHooks: Failed to retrieve 'dexElements' from DexPathList.\");\n        return;\n    }\n\n    // Iterate over every element in the DexPathList to process individual DEX files.\n    for (auto &element : elements) {\n        if (element.get() == nullptr) continue;\n\n        // extract the DexFile Java object from the element.\n        auto java_dex_file =\n            lsplant::JNI_GetObjectFieldOf(env, element, \"dexFile\", \"Ldalvik/system/DexFile;\");\n        if (!java_dex_file) {\n            // Not all elements are guaranteed to have a valid DexFile\n            // (e.g., resource-only elements).\n            LOGW(\"InitHooks: Encountered a dexElement with no associated DexFile.\");\n            continue;\n        }\n\n        // Retrieve the 'mCookie'. In ART, this field stores the pointer (as a long or object)\n        // to the internal native DexFile structure.\n        auto cookie =\n            lsplant::JNI_GetObjectFieldOf(env, java_dex_file, \"mCookie\", \"Ljava/lang/Object;\");\n        if (!cookie) {\n            LOGW(\"InitHooks: Could not retrieve 'mCookie' (native handle) from DexFile.\");\n            continue;\n        }\n\n        // Attempt to modify the internal ART flags for this DEX file.\n        // This effectively whitelists the DEX file, treating it as if it were part of\n        // the BootClassPath, thereby bypassing Hidden API enforcement policies.\n        if (lsplant::MakeDexFileTrusted(env, cookie.get())) {\n            LOGD(\"InitHooks: Successfully elevated trust privileges for DexFile.\");\n        } else {\n            LOGW(\"InitHooks: Failed to elevate trust privileges for DexFile.\");\n        }\n    }\n\n    // -------------------------------------------------------------------------\n    // JNI Bridge Registration\n    // -------------------------------------------------------------------------\n    jni::RegisterResourcesHook(env);\n    jni::RegisterHookBridge(env);\n    jni::RegisterNativeApiBridge(env);\n    jni::RegisterDexParserBridge(env);\n}\n\nlsplant::ScopedLocalRef<jclass> Context::FindClassFromLoader(JNIEnv *env, jobject class_loader,\n                                                             std::string_view class_name) {\n    if (class_loader == nullptr) {\n        return {env, nullptr};\n    }\n    static const auto dex_class_loader_class =\n        lsplant::JNI_NewGlobalRef(env, lsplant::JNI_FindClass(env, \"dalvik/system/DexClassLoader\"));\n    static jmethodID load_class_mid = lsplant::JNI_GetMethodID(\n        env, dex_class_loader_class, \"loadClass\", \"(Ljava/lang/String;)Ljava/lang/Class;\");\n    if (!load_class_mid) {\n        load_class_mid = lsplant::JNI_GetMethodID(env, dex_class_loader_class, \"findClass\",\n                                                  \"(Ljava/lang/String;)Ljava/lang/Class;\");\n    }\n\n    if (load_class_mid) {\n        auto name_str = lsplant::JNI_NewStringUTF(env, class_name.data());\n        auto result = lsplant::JNI_CallObjectMethod(env, class_loader, load_class_mid, name_str);\n        if (result) {\n            return result;\n        }\n    } else {\n        LOGE(\"Could not find DexClassLoader.loadClass / .findClass method ID.\");\n    }\n\n    // Log clearly on failure.\n    if (env->ExceptionCheck()) {\n        env->ExceptionClear();  // Clear exception to prevent app crash\n    }\n    LOGE(\"Class '{}' not found using the provided class loader.\", class_name.data());\n    return {env, nullptr};\n}\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/src/core/native_api.cpp",
    "content": "#include \"core/native_api.h\"\n\n#include <sys/mman.h>\n\n#include <functional>\n#include <list>\n#include <memory>\n#include <mutex>\n\n#include \"common/logging.h\"\n#include \"elf/elf_image.h\"\n#include \"elf/symbol_cache.h\"\n\n/**\n * @file native_api.cpp\n * @brief Implementation of the native module loading and API provisioning system.\n */\n\nusing lsplant::operator\"\"_sym;\n/*\n * ===========================================================================================\n * LSPLANT HOOKING DSL (DOMAIN SPECIFIC LANGUAGE) DOCUMENTATION\n * ===========================================================================================\n *\n * This source file utilizes the 'lsplant' library, which implements a C++20 Hooking DSL.\n * Unlike traditional C-style hooking (which relies on void* casting, manual trampolines,\n * and global function pointers), this DSL uses compile-time metaprogramming to ensure\n * type safety and encapsulate hooking logic.\n *\n * -------------------------------------------------------------------------------------------\n * 1. SYNTAX ANATOMY\n * -------------------------------------------------------------------------------------------\n * The hooking syntax follows this pattern:\n *    \"SYMBOL_NAME\"_sym .hook ->* [] <auto backup> (args...) { ...body... };\n *\n *  A. \"SYMBOL_NAME\"_sym\n *     - This is a C++ User-Defined Literal (UDL). It converts the string literal into a\n *       compile-time 'Symbol' type.\n *     - For C++ mangled names (common in Android system libs), you must provide the full\n *       mangled signature (e.g., \"__dl__Z9do_dlopen...\").\n *\n *  B. Multi-Architecture Support (| Operator)\n *     - Android often requires different symbol names for 32-bit (ARM) and 64-bit (ARM64).\n *     - The DSL supports the pipe operator '|' to select the correct symbol at compile time:\n *       (\"Sym32\"_sym | \"Sym64\"_sym)\n *\n *  C. .hook ->*\n *     - '.hook' accesses the hook injection mechanism.\n *     - '->*' (Member Pointer Operator) is overloaded to bind the symbol to the lambda.\n *\n *  D. The Template Lambda (The Replacement)\n *     - Syntax: [] <lsplant::Backup auto backup> (Type arg1, Type arg2...) { ... }\n *     - This is a C++20 Template Lambda.\n *     - 'backup': Represents the ORIGINAL function (trampoline).\n *                 You call this to execute the original system logic.\n *     - 'args...': Must match the signature of the target function exactly.\n *\n * -------------------------------------------------------------------------------------------\n * 2. EXAMPLE USAGE\n * -------------------------------------------------------------------------------------------\n * inline static auto my_hook =\n *     \"__open\"_sym.hook ->* []<auto backup>(const char* path, int flags) {\n *         // 1. Pre-processing (Before original)\n *         LOGD(\"Opening file: %s\", path);\n *\n *         // 2. Call Original (The \"Backup\")\n *         int result = backup(path, flags);\n *\n *         // 3. Post-processing (After original)\n *         return result;\n *     };\n *\n * -------------------------------------------------------------------------------------------\n * 3. REGISTRATION\n * -------------------------------------------------------------------------------------------\n * Defining the hook variable does not apply it.\n * You must pass the variable to the HookHandler to modify memory: handler(my_hook).\n * ===========================================================================================\n */\n\nnamespace vector::native {\n\nnamespace {\n// Mutex to protect access to the global module lists.\nstd::mutex g_module_registry_mutex;\n// List of callback functions provided by loaded native modules.\nstd::list<NativeOnModuleLoaded> g_module_loaded_callbacks;\n// List of native library filenames that are registered as modules.\nstd::list<std::string> g_module_native_libs;\n\n// A smart pointer to a memory page that will hold the NativeAPIEntries struct.\nstd::unique_ptr<void, std::function<void(void *)>> g_api_page(\n    mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0), [](void *ptr) {\n        if (ptr != MAP_FAILED) {\n            munmap(ptr, 4096);\n        }\n    });\n}  // namespace\n\n// The read-only, statically available Native API entry points for modules.\nconst NativeAPIEntries *g_native_api_entries = nullptr;\n\n/**\n * @brief Initializes the Native API entries struct and makes it read-only.\n */\nvoid InitializeApiEntries() {\n    if (g_api_page.get() == MAP_FAILED) {\n        LOGF(\"Failed to allocate memory for native API entries.\");\n        LOGD(\"Release the memory page pointer %p\", g_api_page.release());\n        return;\n    }\n    auto *entries = new (g_api_page.get()) NativeAPIEntries{\n        .version = 2,\n        .hookFunc = &HookInline,\n        .unhookFunc = &UnhookInline,\n    };\n    if (mprotect(g_api_page.get(), 4096, PROT_READ) != 0) {\n        PLOGE(\"Failed to mprotect API page to read-only\");\n    }\n    g_native_api_entries = entries;\n    LOGI(\"Native API entries initialized and protected.\");\n}\n\nvoid RegisterNativeLib(const std::string &library_name) {\n    static bool is_initialized = []() {\n        InitializeApiEntries();\n        return InstallNativeAPI(lsplant::InitInfo{\n            .inline_hooker =\n                [](void *target, void *replacement) {\n                    void *backup = nullptr;\n                    return HookInline(target, replacement, &backup) == 0 ? backup : nullptr;\n                },\n            .art_symbol_resolver =\n                [](auto symbol) { return ElfSymbolCache::GetLinker()->getSymbAddress(symbol); },\n        });\n    }();\n\n    if (!is_initialized) {\n        LOGE(\"Cannot register module '{}' because native API failed to initialize.\",\n             library_name.c_str());\n        return;\n    }\n\n    std::lock_guard<std::mutex> lock(g_module_registry_mutex);\n    g_module_native_libs.push_back(library_name);\n    LOGD(\"Native module library '{}' has been registered.\", library_name.c_str());\n}\n\nbool HasEnding(std::string_view fullString, std::string_view ending) {\n    if (fullString.length() >= ending.length()) {\n        return (fullString.compare(fullString.length() - ending.length(), std::string_view::npos,\n                                   ending) == 0);\n    }\n    return false;\n}\n\ninline static auto do_dlopen_hook =\n    \"__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv\"_sym.hook->*\n    []<lsplant::Backup auto backup>(const char *name, int flags, const void *extinfo,\n                                    const void *caller_addr) static -> void * {\n    void *handle = backup(name, flags, extinfo, caller_addr);\n    const std::string lib_name = (name != nullptr) ? name : \"null\";\n    LOGV(\"do_dlopen hook triggered for library: '{}'\", lib_name.c_str());\n\n    if (handle == nullptr) return nullptr;\n\n    std::lock_guard<std::mutex> lock(g_module_registry_mutex);\n\n    for (std::string_view module_lib : g_module_native_libs) {\n        if (HasEnding(lib_name, module_lib)) {\n            LOGI(\"Detected registered native module being loaded: '{}'\", lib_name.c_str());\n            void *init_sym = dlsym(handle, \"native_init\");\n            if (init_sym == nullptr) {\n                LOGW(\"Library '{}' matches a module name but does not export 'native_init'.\",\n                     lib_name.c_str());\n                break;\n            }\n            auto native_init = reinterpret_cast<NativeInit>(init_sym);\n            if (auto callback = native_init(g_native_api_entries)) {\n                g_module_loaded_callbacks.push_back(callback);\n                LOGI(\"Initialized native module '{}' and registered its callback.\",\n                     lib_name.c_str());\n            }\n            break;\n        }\n    }\n\n    for (const auto &callback : g_module_loaded_callbacks) {\n        callback(name, handle);\n    }\n\n    return handle;\n};\n\nbool InstallNativeAPI(const lsplant::HookHandler &handler) { return handler(do_dlopen_hook); }\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/src/elf/elf_image.cpp",
    "content": "#include \"elf/elf_image.h\"\n\n#include <fcntl.h>\n#include <linux/xz.h>  // For decompressing .gnu_debugdata\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <cstring>\n#include <utility>  // For std::move\n\n#include \"common/logging.h\"\n\nnamespace vector::native {\n\nnamespace {\n// Helper to safely cast an offset from a base pointer.\ntemplate <typename T>\ninline T PtrOffset(void *base, ptrdiff_t offset) {\n    return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(base) + offset);\n}\n}  // namespace\n\nElfImage::ElfImage(std::string_view lib_name) : path_(lib_name) {\n    if (!findModuleBase()) {\n        base_ = nullptr;  // Ensure base_ is null on failure.\n        return;\n    }\n\n    int fd = open(path_.c_str(), O_RDONLY | O_CLOEXEC);\n    if (fd < 0) {\n        PLOGE(\"Failed to open ELF file: {}\", path_.c_str());\n        return;\n    }\n\n    struct stat file_info;\n    if (fstat(fd, &file_info) < 0) {\n        PLOGE(\"fstat failed for {}\", path_.c_str());\n        close(fd);\n        return;\n    }\n    file_size_ = file_info.st_size;\n\n    file_map_ = mmap(nullptr, file_size_, PROT_READ, MAP_SHARED, fd, 0);\n    close(fd);\n\n    if (file_map_ == MAP_FAILED) {\n        PLOGE(\"mmap failed for {}\", path_.c_str());\n        file_map_ = nullptr;\n        return;\n    }\n\n    header_ = static_cast<ElfW(Ehdr) *>(file_map_);\n    parseHeaders(header_);\n\n    // Check for and handle compressed debug symbols.\n    if (decompressGnuDebugData()) {\n        header_debugdata_ = PtrOffset<ElfW(Ehdr) *>(elf_debugdata_.data(), 0);\n        // Re-parse to find the .symtab and its .strtab from the debug data.\n        parseHeaders(header_debugdata_);\n    }\n}\n\nElfImage::~ElfImage() {\n    if (file_map_ != nullptr) {\n        munmap(file_map_, file_size_);\n    }\n}\n\nvoid ElfImage::parseHeaders(ElfW(Ehdr) * header) {\n    if (!header) return;\n\n    ElfW(Shdr) *section_headers = PtrOffset<ElfW(Shdr) *>(header, header->e_shoff);\n    const char *section_str_table =\n        PtrOffset<const char *>(header, section_headers[header->e_shstrndx].sh_offset);\n\n    for (int i = 0; i < header->e_shnum; ++i) {\n        ElfW(Shdr) *section_h = &section_headers[i];\n        const char *sname = section_str_table + section_h->sh_name;\n\n        switch (section_h->sh_type) {\n        case SHT_DYNSYM:\n            // We only care about the first .dynsym found in the original ELF file.\n            if (dynsym_ == nullptr) {\n                dynsym_ = section_h;\n                dynsym_start_ = PtrOffset<ElfW(Sym) *>(header, section_h->sh_offset);\n            }\n            break;\n        case SHT_SYMTAB:\n            if (strcmp(sname, \".symtab\") == 0) {\n                symtab_start_ = PtrOffset<ElfW(Sym) *>(header, section_h->sh_offset);\n                symtab_count_ = section_h->sh_size / section_h->sh_entsize;\n            }\n            break;\n        case SHT_STRTAB:\n            // The string table for .dynsym is usually the first SHT_STRTAB after .dynsym.\n            // We identify it by checking if dynsym is found but its strtab is not.\n            if (dynsym_ != nullptr && strtab_start_ == nullptr) {\n                strtab_start_ = PtrOffset<const char *>(header, section_h->sh_offset);\n            }\n            // The string table for .symtab is explicitly named \".strtab\".\n            if (strcmp(sname, \".strtab\") == 0) {\n                symtab_str_start_ = PtrOffset<const char *>(header, section_h->sh_offset);\n            }\n            break;\n        case SHT_PROGBITS:\n            // The load bias is the difference between\n            // the virtual address of a loaded segment and its offset in the file.\n\n            // Ensure we skip early sections like .interp or .note\n            // by waiting until after dynsym and strtab are found.\n            if (dynsym_ == nullptr || strtab_start_ == nullptr) break;\n\n            if (!bias_calculated_ && section_h->sh_flags & SHF_ALLOC && section_h->sh_addr > 0) {\n                bias_ = section_h->sh_addr - section_h->sh_offset;\n                bias_calculated_ = true;\n            }\n            break;\n        case SHT_HASH:\n            // Standard ELF hash table.\n            if (nbucket_ == 0) {\n                uint32_t *hash_data = PtrOffset<uint32_t *>(header, section_h->sh_offset);\n                nbucket_ = hash_data[0];\n                // nchain is hash_data[1]\n                bucket_ = &hash_data[2];\n                chain_ = bucket_ + nbucket_;\n            }\n            break;\n        case SHT_GNU_HASH:\n            // GNU-style hash table.\n            if (gnu_nbucket_ == 0) {\n                uint32_t *hash_data = PtrOffset<uint32_t *>(header, section_h->sh_offset);\n                gnu_nbucket_ = hash_data[0];\n                gnu_symndx_ = hash_data[1];\n                gnu_bloom_size_ = hash_data[2];\n                gnu_shift2_ = hash_data[3];\n                gnu_bloom_filter_ = reinterpret_cast<uintptr_t *>(&hash_data[4]);\n                gnu_bucket_ = reinterpret_cast<uint32_t *>(gnu_bloom_filter_ + gnu_bloom_size_);\n                gnu_chain_ = gnu_bucket_ + gnu_nbucket_;\n            }\n            break;\n        }\n    }\n}\n\nbool ElfImage::decompressGnuDebugData() {\n    ElfW(Shdr) *section_headers = PtrOffset<ElfW(Shdr) *>(header_, header_->e_shoff);\n    const char *section_str_table =\n        PtrOffset<const char *>(header_, section_headers[header_->e_shstrndx].sh_offset);\n    ElfW(Off) debugdata_offset = 0;\n    ElfW(Off) debugdata_size = 0;\n\n    for (int i = 0; i < header_->e_shnum; ++i) {\n        if (strcmp(section_str_table + section_headers[i].sh_name, \".gnu_debugdata\") == 0) {\n            debugdata_offset = section_headers[i].sh_offset;\n            debugdata_size = section_headers[i].sh_size;\n            break;\n        }\n    }\n\n    if (debugdata_offset == 0 || debugdata_size == 0) {\n        return false;  // Section not found.\n    }\n    LOGD(\"Found .gnu_debugdata section in {} ({} bytes). Decompressing...\", path_.c_str(),\n         debugdata_size);\n\n    xz_crc32_init();\n    struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26);\n    if (!dec) return false;\n\n    struct xz_buf buf;\n    buf.in = PtrOffset<const uint8_t *>(header_, debugdata_offset);\n    buf.in_pos = 0;\n    buf.in_size = debugdata_size;\n\n    elf_debugdata_.resize(debugdata_size * 8);  // Initial guess\n    buf.out = reinterpret_cast<uint8_t *>(elf_debugdata_.data());\n    buf.out_pos = 0;\n    buf.out_size = elf_debugdata_.size();\n\n    while (true) {\n        enum xz_ret ret = xz_dec_run(dec, &buf);\n        if (ret == XZ_STREAM_END) {\n            elf_debugdata_.resize(buf.out_pos);\n            xz_dec_end(dec);\n            LOGD(\"Successfully decompressed .gnu_debugdata ({} bytes)\", elf_debugdata_.size());\n            return true;\n        }\n        if (ret != XZ_OK) {\n            LOGE(\"XZ decompression failed with code {}\", (int)ret);\n            xz_dec_end(dec);\n            return false;\n        }\n        if (buf.out_pos == buf.out_size) {\n            elf_debugdata_.resize(elf_debugdata_.size() * 2);\n            // Reset pointer to the potentially new base address\n            buf.out = reinterpret_cast<uint8_t *>(elf_debugdata_.data());\n            // Update the total capacity\n            buf.out_size = elf_debugdata_.size();\n        }\n    }\n}\n\nElfW(Addr) ElfImage::getSymbOffset(std::string_view name, uint32_t gnu_hash,\n                                   uint32_t elf_hash) const {\n    if (auto offset = gnuLookup(name, gnu_hash); offset > 0) {\n        return offset;\n    } else if (offset = elfLookup(name, elf_hash); offset > 0) {\n        return offset;\n    } else if (offset = linearLookup(name); offset > 0) {\n        return offset;\n    } else {\n        return 0;\n    }\n}\n\nElfW(Addr) ElfImage::gnuLookup(std::string_view name, uint32_t hash) const {\n    if (gnu_nbucket_ == 0) return 0;\n\n    constexpr auto bloom_mask_bits = sizeof(ElfW(Addr)) * 8;\n    auto bloom_word = gnu_bloom_filter_[(hash / bloom_mask_bits) % gnu_bloom_size_];\n    uintptr_t mask =\n        (1ULL << (hash % bloom_mask_bits)) | (1ULL << ((hash >> gnu_shift2_) % bloom_mask_bits));\n\n    if ((bloom_word & mask) != mask) {\n        return 0;  // Not in bloom filter, definitely not here.\n    }\n\n    uint32_t sym_idx = gnu_bucket_[hash % gnu_nbucket_];\n    if (sym_idx < gnu_symndx_) return 0;\n\n    do {\n        ElfW(Sym) *sym = dynsym_start_ + sym_idx;\n        if (((gnu_chain_[sym_idx - gnu_symndx_] ^ hash) >> 1) == 0) {\n            if (std::string_view(strtab_start_ + sym->st_name) == name) {\n                return sym->st_value;\n            }\n        }\n    } while ((gnu_chain_[sym_idx++ - gnu_symndx_] & 1) == 0);\n\n    return 0;\n}\n\nElfW(Addr) ElfImage::elfLookup(std::string_view name, uint32_t hash) const {\n    if (nbucket_ == 0) return 0;\n\n    for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {\n        ElfW(Sym) *sym = dynsym_start_ + n;\n        if (std::string_view(strtab_start_ + sym->st_name) == name) {\n            return sym->st_value;\n        }\n    }\n    return 0;\n}\n\nvoid ElfImage::ensureLinearMapInitialized() const {\n    // Lazily parse the .symtab section and build a map for faster lookups.\n    if (!symtabs_.empty() || !symtab_start_ || !symtab_str_start_) {\n        return;\n    }\n\n    for (ElfW(Off) i = 0; i < symtab_count_; ++i) {\n        auto *sym = &symtab_start_[i];\n        unsigned int st_type = ELF_ST_TYPE(sym->st_info);\n        // We only care about function or object symbols that have a size.\n        if ((st_type == STT_FUNC || st_type == STT_OBJECT) && sym->st_size > 0) {\n            const char *st_name = symtab_str_start_ + sym->st_name;\n            symtabs_.emplace(st_name, sym);\n        }\n    }\n}\n\nElfW(Addr) ElfImage::linearLookup(std::string_view name) const {\n    ensureLinearMapInitialized();\n    auto it = symtabs_.find(name);\n    if (it != symtabs_.end()) {\n        return it->second->st_value;\n    }\n    return 0;\n}\n\nstd::vector<ElfW(Addr)> ElfImage::linearRangeLookup(std::string_view name) const {\n    ensureLinearMapInitialized();\n    std::vector<ElfW(Addr)> res;\n    for (auto [it, end] = symtabs_.equal_range(name); it != end; ++it) {\n        res.emplace_back(it->second->st_value);\n    }\n    return res;\n}\n\nElfW(Addr) ElfImage::prefixLookupFirst(std::string_view prefix) const {\n    ensureLinearMapInitialized();\n    // lower_bound finds the first element not less than the prefix.\n    auto it = symtabs_.lower_bound(prefix);\n    if (it != symtabs_.end() && it->first.starts_with(prefix)) {\n        return it->second->st_value;\n    }\n    return 0;\n}\n\nbool ElfImage::findModuleBase() {\n    // A helper struct to hold parsed map entry data.\n    struct MapEntry {\n        uintptr_t start_addr;\n        char perms[5] = {0};\n        std::string pathname;\n    };\n\n    FILE *maps = fopen(\"/proc/self/maps\", \"r\");\n    if (!maps) {\n        PLOGE(\"Failed to open /proc/self/maps\");\n        return false;\n    }\n\n    char line_buffer[512];\n    std::vector<MapEntry> filtered_list;\n\n    // Filter all entries containing the library name.\n    while (fgets(line_buffer, sizeof(line_buffer), maps)) {\n        if (strstr(line_buffer, path_.c_str())) {\n            unsigned long long temp_start;\n            char path_buffer[256] = {0};\n            char p[5] = {0};\n            int items_parsed =\n                sscanf(line_buffer, \"%llx-%*x %4s %*x %*s %*d %255s\", &temp_start, p, path_buffer);\n\n            if (items_parsed >= 2) {\n                MapEntry entry;\n                entry.start_addr = static_cast<uintptr_t>(temp_start);\n                strncpy(entry.perms, p, 4);\n                if (items_parsed == 3) entry.pathname = path_buffer;\n                filtered_list.push_back(std::move(entry));\n                LOGD(\"Found module entry: {}\", line_buffer);\n            }\n        }\n    }\n    fclose(maps);\n\n    if (filtered_list.empty()) {\n        LOGE(\"Could not find any mappings for {}\", path_.c_str());\n        return false;\n    }\n\n    const MapEntry *found_block = nullptr;\n\n    // Search for the first `r--p` whose next entry is `r-xp`.\n    // This is the most reliable pattern for `libart.so`.\n    for (size_t i = 0; i + 1 < filtered_list.size(); ++i) {\n        if (strcmp(filtered_list[i].perms, \"r--p\") == 0 &&\n            strcmp(filtered_list[i + 1].perms, \"r-xp\") == 0) {\n            found_block = &filtered_list[i];\n            break;\n        }\n    }\n\n    // If the pattern was not found, find the first `r-xp` entry.\n    if (!found_block) {\n        for (const auto &entry : filtered_list) {\n            if (strcmp(entry.perms, \"r-xp\") == 0) {\n                found_block = &entry;\n                break;\n            }\n        }\n    }\n\n    // If still no match, take the very first entry found.\n    if (!found_block) {\n        found_block = &filtered_list[0];\n    }\n\n    // Use the starting address of the found block as the base address.\n    base_ = reinterpret_cast<void *>(found_block->start_addr);\n    // Update path to the canonical one from the maps file.\n    if (!found_block->pathname.empty()) {\n        path_ = found_block->pathname;\n    }\n\n    LOGD(\"Found base for {} at {:#x}\", path_.c_str(), found_block->start_addr);\n    return true;\n}\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/src/elf/symbol_cache.cpp",
    "content": "#include \"elf/symbol_cache.h\"\n\n#include <mutex>\n\n#include \"common/config.h\"\n#include \"elf/elf_image.h\"\n\nnamespace vector::native {\n\nnamespace {\n// Each cached ElfImage gets its own unique_ptr and a mutex to guard its\n// initialization.\nstd::unique_ptr<const ElfImage> g_art_image = nullptr;\nstd::mutex g_art_mutex;\n\nstd::unique_ptr<const ElfImage> g_binder_image = nullptr;\nstd::mutex g_binder_mutex;\n\nstd::unique_ptr<const ElfImage> g_linker_image = nullptr;\nstd::mutex g_linker_mutex;\n}  // namespace\n\nconst ElfImage *ElfSymbolCache::GetArt() {\n    // Double-checked locking pattern for performance.\n    // The first check is lock-free.\n    if (g_art_image) {\n        return g_art_image.get();\n    }\n\n    // If it's null, acquire the lock to perform the initialization safely.\n    std::lock_guard<std::mutex> lock(g_art_mutex);\n\n    // Check again inside the lock in case another thread initialized it\n    // while we were waiting for the lock.\n    if (!g_art_image) {\n        g_art_image = std::make_unique<ElfImage>(kArtLibraryName);\n        if (!g_art_image->IsValid()) {\n            g_art_image.reset();  // Release if invalid.\n        }\n    }\n    return g_art_image.get();\n}\n\nconst ElfImage *ElfSymbolCache::GetLibBinder() {\n    if (g_binder_image) {\n        return g_binder_image.get();\n    }\n    std::lock_guard<std::mutex> lock(g_binder_mutex);\n    if (!g_binder_image) {\n        g_binder_image = std::make_unique<ElfImage>(kBinderLibraryName);\n        if (!g_binder_image->IsValid()) {\n            g_binder_image.reset();\n        }\n    }\n    return g_binder_image.get();\n}\n\nconst ElfImage *ElfSymbolCache::GetLinker() {\n    if (g_linker_image) {\n        return g_linker_image.get();\n    }\n    std::lock_guard<std::mutex> lock(g_linker_mutex);\n    if (!g_linker_image) {\n        g_linker_image = std::make_unique<ElfImage>(kLinkerPath);\n        if (!g_linker_image->IsValid()) {\n            g_linker_image.reset();\n        }\n    }\n    return g_linker_image.get();\n}\n\nbool ElfSymbolCache::ClearCache(const ElfImage *image_to_clear) {\n    if (!image_to_clear) {\n        return false;\n    }\n\n    // This \"lock, check, then reset\" pattern must be atomic for each cache entry.\n    // We check each cache one by one.\n\n    // Check ART cache\n    {\n        std::lock_guard<std::mutex> lock(g_art_mutex);\n        if (image_to_clear == g_art_image.get()) {\n            g_art_image.reset();\n            return true;  // Found and cleared, no need to check others.\n        }\n    }\n\n    // Check Binder cache\n    {\n        std::lock_guard<std::mutex> lock(g_binder_mutex);\n        if (image_to_clear == g_binder_image.get()) {\n            g_binder_image.reset();\n            return true;\n        }\n    }\n\n    // Check Linker cache\n    {\n        std::lock_guard<std::mutex> lock(g_linker_mutex);\n        if (image_to_clear == g_linker_image.get()) {\n            g_linker_image.reset();\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid ElfSymbolCache::ClearCache() {\n    // Acquire all locks to ensure no other thread is currently initializing.\n    std::lock_guard<std::mutex> art_lock(g_art_mutex);\n    std::lock_guard<std::mutex> binder_lock(g_binder_mutex);\n    std::lock_guard<std::mutex> linker_lock(g_linker_mutex);\n    g_art_image.reset();\n    g_binder_image.reset();\n    g_linker_image.reset();\n}\n\n}  // namespace vector::native\n"
  },
  {
    "path": "native/src/jni/dex_parser_bridge.cpp",
    "content": "#include <parallel_hashmap/phmap.h>\n#include <slicer/reader.h>\n\n#include <list>\n\n#include \"jni/jni_bridge.h\"\n#include \"jni/jni_hooks.h\"\n\n/**\n * @file dex_parser_bridge.cpp\n * @brief Implements a JNI bridge to a native DEX file parser.\n *\n * This bridge provides a memory-efficient way for Java code to parse Android DEX files.\n * It avoids creating a complete object representation of the DEX file in memory,\n * which can be very large.\n *\n * It employs a visitor pattern:\n * 1.  The `openDex` method performs an initial parse of the DEX file's main sections\n *      (strings, types, fields, methods, classes) and returns them\n *      to the Java caller as primitive arrays.\n *      It stores the detailed parsed data in a native `DexParser` object.\n * 2.  The `visitClass` method then iterates through the parsed classes and\n *     invokes callback methods on a Java \"visitor\" object for\n *     each class, field, and method.\n *\n * This approach minimizes JNI overhead and memory consumption by processing data\n * in a streaming fashion and only creating Java objects as needed for the visitor callbacks.\n */\n\nnamespace {\n// Type aliases for representing DEX encoded values and annotations.\n// These structures temporarily hold parsed annotation data before it's converted to Java objects.\n\n// A DEX encoded value, represented as a tuple of its type and raw byte data.\nusing Value = std::tuple<jint /*type*/, std::vector<jbyte> /*data*/>;\n// A DEX encoded array, which is a vector of encoded values.\nusing Array = std::vector<Value>;\n// A list of encoded arrays. A list is used because its elements won't be\n// reallocated, which is important when indices are stored.\nusing ArrayList = std::list<Array>;\n// An element of an annotation, consisting of a name (index into string table) and a value.\nusing Element = std::tuple<jint /*name*/, Value>;\n// A list of annotation elements.\nusing ElementList = std::vector<Element>;\n// A DEX annotation, containing its visibility, type, and a list of its elements.\nusing Annotation = std::tuple<jint /*vis*/, jint /*type*/, ElementList>;\n// A list of annotations.\nusing AnnotationList = std::vector<Annotation>;\n\n/**\n * @class DexParser\n * @brief Extends slicer's dex::Reader to hold parsed class, method, and annotation data.\n *\n * This class serves as the main native handle for a parsed DEX file.\n * It stores structured data that has been read from the DEX file,\n * making it readily available for the `visitClass` function.\n */\nclass DexParser : public dex::Reader {\npublic:\n    DexParser(const dex::u1 *data, size_t size) : dex::Reader(data, size, nullptr, 0) {}\n\n    /**\n     * @struct ClassData\n     * @brief Holds all relevant information for a single class definition.\n     *\n     * This structure is populated during the `openDex` phase and contains indices\n     * pointing to the DEX file's various data pools (types, fields, methods).\n     */\n    struct ClassData {\n        std::vector<jint> interfaces;\n        std::vector<jint> static_fields;\n        std::vector<jint> static_fields_access_flags;\n        std::vector<jint> instance_fields;\n        std::vector<jint> instance_fields_access_flags;\n        std::vector<jint> direct_methods;\n        std::vector<jint> direct_methods_access_flags;\n        std::vector<const dex::Code *> direct_methods_code;  // Pointers to method bytecode\n        std::vector<jint> virtual_methods;\n        std::vector<jint> virtual_methods_access_flags;\n        std::vector<const dex::Code *> virtual_methods_code;  // Pointers to method bytecode\n        std::vector<jint> annotations;\n    };\n\n    /**\n     * @struct MethodBody\n     * @brief Lazily-parsed information from a method's bytecode.\n     *\n     * This data is only computed when a method is visited in `visitClass`,\n     * saving significant processing time if the caller is not interested in method body details.\n     */\n    struct MethodBody {\n        bool loaded = false;  // Flag to indicate if this body has been parsed yet.\n        std::vector<jint> referred_strings;\n        std::vector<jint> accessed_fields;  // Fields read from (iget/sget)\n        std::vector<jint> assigned_fields;  // Fields written to (iput/sput)\n        std::vector<jint> invoked_methods;\n        std::vector<jbyte> opcodes;\n    };\n\n    // Parsed data storage\n    std::vector<ClassData> class_data;  // One entry per ClassDef in the DEX file.\n    // Mappings from an item's index to a list of annotation indices.\n    // Using phmap::flat_hash_map for fast lookups.\n    phmap::flat_hash_map<jint, std::vector<jint>> field_annotations;\n    phmap::flat_hash_map<jint, std::vector<jint>> method_annotations;\n    phmap::flat_hash_map<jint, std::vector<jint>> parameter_annotations;\n\n    // Lazily populated map of method index to its parsed body.\n    phmap::flat_hash_map<jint, MethodBody> method_bodies;\n};\n\n/**\n * @brief Parses a variable-length integer from the DEX byte stream.\n * @tparam T The integral type to parse (e.g., int8_t, int32_t).\n * @param pptr Pointer to the current position in the byte stream.\n * @param size The number of bytes to read (1 to sizeof(T)).\n * @return A vector of bytes containing the parsed value.\n */\ntemplate <class T>\nstatic std::vector<jbyte> ParseIntValue(const dex::u1 **pptr, size_t size) {\n    static_assert(std::is_integral<T>::value, \"must be an integral type\");\n    std::vector<jbyte> ret(sizeof(T));\n    // Use reinterpret_cast to type-pun the byte vector's data into the target integer type.\n    T &value = *reinterpret_cast<T *>(ret.data());\n    value = 0;  // Ensure starting from a clean state.\n    for (size_t i = 0; i < size; ++i) {\n        value |= T(*(*pptr)++) << (i * 8);\n    }\n\n    // If the type is signed and we read fewer bytes than its full size,\n    // we need to manually sign-extend the value.\n    if constexpr (std::is_signed_v<T>) {\n        size_t shift = (sizeof(T) - size) * 8;\n        if (shift > 0) {\n            value = T(value << shift) >> shift;\n        }\n    }\n    return ret;\n}\n\n/**\n * @brief Parses a variable-length float from the DEX byte stream.\n * @tparam T The floating-point type to parse (float or double).\n * @param pptr Pointer to the current position in the byte stream.\n * @param size The number of bytes to read.\n * @return A vector of bytes containing the parsed value.\n */\ntemplate <class T>\nstatic std::vector<jbyte> ParseFloatValue(const dex::u1 **pptr, size_t size) {\n    std::vector<jbyte> ret(sizeof(T), 0);\n    T &value = *reinterpret_cast<T *>(ret.data());\n    // The value is right-padded with zero bytes, so we copy into the higher-order bytes.\n    int start_byte = sizeof(T) - size;\n    for (dex::u1 *p = reinterpret_cast<dex::u1 *>(&value) + start_byte; size > 0; --size) {\n        *p++ = *(*pptr)++;\n    }\n    return ret;\n}\n\n// Forward declarations for recursive parsing functions.\nAnnotation ParseAnnotation(const dex::u1 **annotation, AnnotationList &annotation_list,\n                           ArrayList &array_list);\n\nArray ParseArray(const dex::u1 **array, AnnotationList &annotation_list, ArrayList &array_list);\n\n/**\n * @brief Parses a single `encoded_value` from the byte stream.\n * This is the core of the annotation parsing logic and\n * handles all possible value types recursively.\n */\nValue ParseValue(const dex::u1 **value, AnnotationList &annotation_list, ArrayList &array_list) {\n    Value res;\n    auto &[type, value_content] = res;\n    auto header = *(*value)++;\n    type = header & dex::kEncodedValueTypeMask;\n    dex::u1 arg = header >> dex::kEncodedValueArgShift;\n    switch (type) {\n    // For numeric types, `arg` is `size - 1`.\n    case dex::kEncodedByte:\n        value_content = ParseIntValue<int8_t>(value, arg + 1);\n        break;\n    case dex::kEncodedShort:\n        value_content = ParseIntValue<int16_t>(value, arg + 1);\n        break;\n    case dex::kEncodedChar:\n        value_content = ParseIntValue<uint16_t>(value, arg + 1);\n        break;\n    case dex::kEncodedInt:\n        value_content = ParseIntValue<int32_t>(value, arg + 1);\n        break;\n    case dex::kEncodedLong:\n        value_content = ParseIntValue<int64_t>(value, arg + 1);\n        break;\n    case dex::kEncodedFloat:\n        value_content = ParseFloatValue<float>(value, arg + 1);\n        break;\n    case dex::kEncodedDouble:\n        value_content = ParseFloatValue<double>(value, arg + 1);\n        break;\n    // For index types, the value is the index itself.\n    case dex::kEncodedMethodType:\n    case dex::kEncodedMethodHandle:\n    case dex::kEncodedString:\n    case dex::kEncodedType:\n    case dex::kEncodedField:\n    case dex::kEncodedMethod:\n    case dex::kEncodedEnum:\n        value_content = ParseIntValue<uint32_t>(value, arg + 1);\n        break;\n    // For complex types, we parse them recursively and store an index to the\n    // parsed object.\n    case dex::kEncodedArray:\n        value_content.resize(sizeof(jint));\n        *reinterpret_cast<jint *>(value_content.data()) = static_cast<jint>(array_list.size());\n        array_list.emplace_back(ParseArray(value, annotation_list, array_list));\n        break;\n    case dex::kEncodedAnnotation:\n        value_content.resize(sizeof(jint));\n        *reinterpret_cast<jint *>(value_content.data()) = static_cast<jint>(annotation_list.size());\n        annotation_list.emplace_back(ParseAnnotation(value, annotation_list, array_list));\n        break;\n    case dex::kEncodedNull:\n        // No value content needed.\n        break;\n    case dex::kEncodedBoolean:\n        // The boolean value is stored in the `arg` part of the header.\n        value_content = {static_cast<jbyte>(arg == 1)};\n        break;\n    default:\n        // This should never be reached for a valid DEX file.\n        __builtin_unreachable();\n    }\n    return res;\n}\n\n/**\n * @brief Parses an `encoded_annotation` structure.\n */\nAnnotation ParseAnnotation(const dex::u1 **annotation, AnnotationList &annotation_list,\n                           ArrayList &array_list) {\n    Annotation ret = {dex::kVisibilityEncoded, dex::ReadULeb128(annotation), ElementList{}};\n    auto &[vis, type, element_list] = ret;\n    auto size = dex::ReadULeb128(annotation);\n    element_list.resize(size);\n    for (size_t j = 0; j < size; ++j) {\n        auto &[name, value] = element_list[j];\n        name = static_cast<jint>(dex::ReadULeb128(annotation));\n        value = ParseValue(annotation, annotation_list, array_list);\n    }\n    return ret;\n}\n\n/**\n * @brief Parses an `encoded_array` structure.\n */\nArray ParseArray(const dex::u1 **array, AnnotationList &annotation_list, ArrayList &array_list) {\n    auto size = dex::ReadULeb128(array);\n    Array ret;\n    ret.reserve(size);\n    for (size_t i = 0; i < size; ++i) {\n        ret.emplace_back(ParseValue(array, annotation_list, array_list));\n    }\n    return ret;\n}\n\n/**\n * @brief Parses an `AnnotationSetItem`, which is a collection of annotations.\n */\nvoid ParseAnnotationSet(dex::Reader &dex, AnnotationList &annotation_list, ArrayList &array_list,\n                        std::vector<jint> &indices, const dex::AnnotationSetItem *annotation_set) {\n    if (annotation_set == nullptr) {\n        return;\n    }\n    for (size_t i = 0; i < annotation_set->size; ++i) {\n        auto *item = dex.dataPtr<dex::AnnotationItem>(annotation_set->entries[i]);\n        auto *annotation_data = item->annotation;\n        // Store the index of the new annotation in the output list.\n        indices.emplace_back(annotation_list.size());\n        // Parse the annotation and add it to the global list.\n        auto &[visibility, type, element_list] = annotation_list.emplace_back(\n            ParseAnnotation(&annotation_data, annotation_list, array_list));\n        // The visibility is stored in the item, not the encoded annotation itself.\n        visibility = item->visibility;\n    }\n}\n}  // namespace\n\nnamespace vector::native::jni {\n/**\n * @brief JNI method to open a DEX file and perform initial parsing.\n * @param data A direct java.nio.ByteBuffer containing the DEX file.\n * @param args A jlongArray used for passing arguments.\n *             args[0] is an output parameter to store the native DexParser pointer (cookie).\n *             args[1] is an input flag to control whether to parse annotations.\n * @return A java.lang.Object[] array containing the top-level DEX structures.\n */\nVECTOR_DEF_NATIVE_METHOD(jobject, DexParserBridge, openDex, jobject data, jlongArray args) {\n    auto dex_size = env->GetDirectBufferCapacity(data);\n    if (dex_size == -1) {\n        env->ThrowNew(env->FindClass(\"java/io/IOException\"),\n                      \"DEX data must be in a direct ByteBuffer\");\n        return nullptr;\n    }\n    auto *dex_data = env->GetDirectBufferAddress(data);\n    if (dex_data == nullptr) {\n        env->ThrowNew(env->FindClass(\"java/io/IOException\"), \"Failed to get direct buffer address\");\n        return nullptr;\n    }\n\n    // Create the native parser object.\n    // This will be the handle for subsequent calls.\n    auto *dex_reader = new DexParser(reinterpret_cast<dex::u1 *>(dex_data), dex_size);\n    auto *args_ptr = env->GetLongArrayElements(args, nullptr);\n    auto include_annotations = args_ptr[1];\n    env->ReleaseLongArrayElements(args, args_ptr, JNI_ABORT);\n    // Store the pointer to the native object in the first element of the args array.\n    // This \"cookie\" will be passed back to other native methods.\n    env->SetLongArrayRegion(args, 0, 1, reinterpret_cast<const jlong *>(&dex_reader));\n    auto &dex = *dex_reader;\n    if (dex.IsCompact()) {\n        env->ThrowNew(env->FindClass(\"java/io/IOException\"), \"Compact dex is not supported\");\n        delete dex_reader;  // Clean up before returning.\n        return nullptr;\n    }\n\n    // Find classes needed for creating Java objects.\n    auto object_class = env->FindClass(\"java/lang/Object\");\n    auto string_class = env->FindClass(\"java/lang/String\");\n    auto int_array_class = env->FindClass(\"[I\");\n    // This is the main output array that will be returned to Java.\n    auto out = env->NewObjectArray(8, object_class, nullptr);\n\n    // 1. Parse String IDs\n    auto out0 =\n        env->NewObjectArray(static_cast<jint>(dex.StringIds().size()), string_class, nullptr);\n    auto strings = dex.StringIds();\n    for (size_t i = 0; i < strings.size(); ++i) {\n        const auto *ptr = dex.dataPtr<dex::u1>(strings[i].string_data_off);\n        // The string data is MUTF-8 encoded. We skip the length prefix.\n        [[maybe_unused]] size_t len = dex::ReadULeb128(&ptr);\n        auto str = env->NewStringUTF(reinterpret_cast<const char *>(ptr));\n        env->SetObjectArrayElement(out0, static_cast<jint>(i), str);\n        env->DeleteLocalRef(str);\n    }\n    env->SetObjectArrayElement(out, 0, out0);\n    env->DeleteLocalRef(out0);\n\n    // 2. Parse Type IDs\n    auto types = dex.TypeIds();\n    auto out1 = env->NewIntArray(static_cast<jint>(types.size()));\n    auto *out1_ptr = env->GetIntArrayElements(out1, nullptr);\n    for (size_t i = 0; i < types.size(); ++i) {\n        out1_ptr[i] = static_cast<jint>(types[i].descriptor_idx);  // Index into String table\n    }\n    env->ReleaseIntArrayElements(out1, out1_ptr, 0);\n    env->SetObjectArrayElement(out, 1, out1);\n    env->DeleteLocalRef(out1);\n\n    // 3. Parse Proto IDs (Method Prototypes)\n    auto protos = dex.ProtoIds();\n    auto out2 = env->NewObjectArray(static_cast<jint>(protos.size()), int_array_class, nullptr);\n    auto empty_type_list = dex::TypeList{.size = 0, .list = {}};\n    for (size_t i = 0; i < protos.size(); ++i) {\n        auto &proto = protos[i];\n        const auto &params = proto.parameters_off\n                                 ? *dex.dataPtr<dex::TypeList>(proto.parameters_off)\n                                 : empty_type_list;\n\n        auto out2i = env->NewIntArray(static_cast<jint>(2 + params.size));\n        auto *out2i_ptr = env->GetIntArrayElements(out2i, nullptr);\n        out2i_ptr[0] = static_cast<jint>(proto.shorty_idx);\n        out2i_ptr[1] = static_cast<jint>(proto.return_type_idx);\n        for (size_t j = 0; j < params.size; ++j) {\n            out2i_ptr[2 + j] = static_cast<jint>(params.list[j].type_idx);\n        }\n        env->ReleaseIntArrayElements(out2i, out2i_ptr, 0);\n        env->SetObjectArrayElement(out2, static_cast<jint>(i), out2i);\n        env->DeleteLocalRef(out2i);\n    }\n    env->SetObjectArrayElement(out, 2, out2);\n    env->DeleteLocalRef(out2);\n\n    // 4. Parse Field IDs\n    auto fields = dex.FieldIds();\n    auto out3 = env->NewIntArray(static_cast<jint>(3 * fields.size()));\n    auto *out3_ptr = env->GetIntArrayElements(out3, nullptr);\n    for (size_t i = 0; i < fields.size(); ++i) {\n        auto &field = fields[i];\n        out3_ptr[3 * i] = static_cast<jint>(field.class_idx);     // Defining class type index\n        out3_ptr[3 * i + 1] = static_cast<jint>(field.type_idx);  // Field type index\n        out3_ptr[3 * i + 2] = static_cast<jint>(field.name_idx);  // Field name string index\n    }\n    env->ReleaseIntArrayElements(out3, out3_ptr, 0);\n    env->SetObjectArrayElement(out, 3, out3);\n    env->DeleteLocalRef(out3);\n\n    // 5. Parse Method IDs\n    auto methods = dex.MethodIds();\n    auto out4 = env->NewIntArray(static_cast<jint>(3 * methods.size()));\n    auto *out4_ptr = env->GetIntArrayElements(out4, nullptr);\n    for (size_t i = 0; i < methods.size(); ++i) {\n        out4_ptr[3 * i] = static_cast<jint>(methods[i].class_idx);      // Defining class type index\n        out4_ptr[3 * i + 1] = static_cast<jint>(methods[i].proto_idx);  // Method prototype index\n        out4_ptr[3 * i + 2] = static_cast<jint>(methods[i].name_idx);   // Method name string index\n    }\n    env->ReleaseIntArrayElements(out4, out4_ptr, 0);\n    env->SetObjectArrayElement(out, 4, out4);\n    env->DeleteLocalRef(out4);\n\n    // 6. Parse Class Definitions and their data\n    auto classes = dex.ClassDefs();\n    dex.class_data.resize(classes.size());\n\n    // These lists will store all annotations found in the DEX file.\n    AnnotationList annotation_list;\n    ArrayList array_list;\n\n    for (size_t i = 0; i < classes.size(); ++i) {\n        auto &class_def = classes[i];\n\n        // Pointers to various parts of the class data. Initialize to safe defaults.\n        dex::u4 static_fields_count = 0;\n        dex::u4 instance_fields_count = 0;\n        dex::u4 direct_methods_count = 0;\n        dex::u4 virtual_methods_count = 0;\n        const dex::u1 *class_data_ptr = nullptr;\n\n        const dex::AnnotationsDirectoryItem *annotations = nullptr;\n        const dex::AnnotationSetItem *class_annotation = nullptr;\n        dex::u4 field_annotations_count = 0;\n        dex::u4 method_annotations_count = 0;\n        dex::u4 parameter_annotations_count = 0;\n\n        auto &class_data = dex.class_data[i];\n\n        // Parse implemented interfaces.\n        if (class_def.interfaces_off) {\n            auto defined_interfaces = dex.dataPtr<dex::TypeList>(class_def.interfaces_off);\n            class_data.interfaces.resize(defined_interfaces->size);\n            for (size_t k = 0; k < class_data.interfaces.size(); ++k) {\n                class_data.interfaces[k] = defined_interfaces->list[k].type_idx;\n            }\n        }\n\n        // Locate the annotations directory for this class, if it exists.\n        if (class_def.annotations_off != 0) {\n            annotations = dex.dataPtr<dex::AnnotationsDirectoryItem>(class_def.annotations_off);\n            if (annotations->class_annotations_off != 0) {\n                class_annotation =\n                    dex.dataPtr<dex::AnnotationSetItem>(annotations->class_annotations_off);\n            }\n            field_annotations_count = annotations->fields_size;\n            method_annotations_count = annotations->methods_size;\n            parameter_annotations_count = annotations->parameters_size;\n        }\n\n        // Read the core class data: fields and methods.\n        if (class_def.class_data_off != 0) {\n            class_data_ptr = dex.dataPtr<dex::u1>(class_def.class_data_off);\n            static_fields_count = dex::ReadULeb128(&class_data_ptr);\n            instance_fields_count = dex::ReadULeb128(&class_data_ptr);\n            direct_methods_count = dex::ReadULeb128(&class_data_ptr);\n            virtual_methods_count = dex::ReadULeb128(&class_data_ptr);\n            // Pre-allocate vectors to improve performance.\n            class_data.static_fields.resize(static_fields_count);\n            class_data.static_fields_access_flags.resize(static_fields_count);\n            class_data.instance_fields.resize(instance_fields_count);\n            class_data.instance_fields_access_flags.resize(instance_fields_count);\n            class_data.direct_methods.resize(direct_methods_count);\n            class_data.direct_methods_access_flags.resize(direct_methods_count);\n            class_data.direct_methods_code.resize(direct_methods_count);\n            class_data.virtual_methods.resize(virtual_methods_count);\n            class_data.virtual_methods_access_flags.resize(virtual_methods_count);\n            class_data.virtual_methods_code.resize(virtual_methods_count);\n        }\n\n        // Now, decode the field and method lists.\n        if (class_data_ptr) {\n            // Static fields\n            for (size_t k = 0, field_idx = 0; k < static_fields_count; ++k) {\n                field_idx +=\n                    dex::ReadULeb128(&class_data_ptr);  // field_idx is a diff from previous\n                class_data.static_fields[k] = static_cast<jint>(field_idx);\n                class_data.static_fields_access_flags[k] =\n                    static_cast<jint>(dex::ReadULeb128(&class_data_ptr));\n            }\n\n            // Instance fields\n            for (size_t k = 0, field_idx = 0; k < instance_fields_count; ++k) {\n                field_idx += dex::ReadULeb128(&class_data_ptr);\n                class_data.instance_fields[k] = static_cast<jint>(field_idx);\n                class_data.instance_fields_access_flags[k] =\n                    static_cast<jint>(dex::ReadULeb128(&class_data_ptr));\n            }\n\n            // Direct methods (static, private, constructors)\n            for (size_t k = 0, method_idx = 0; k < direct_methods_count; ++k) {\n                method_idx += dex::ReadULeb128(&class_data_ptr);\n                class_data.direct_methods[k] = static_cast<jint>(method_idx);\n                class_data.direct_methods_access_flags[k] =\n                    static_cast<jint>(dex::ReadULeb128(&class_data_ptr));\n                auto code_off = dex::ReadULeb128(&class_data_ptr);\n                class_data.direct_methods_code[k] =\n                    code_off ? dex.dataPtr<dex::Code>(code_off) : nullptr;\n            }\n\n            // Virtual methods\n            for (size_t k = 0, method_idx = 0; k < virtual_methods_count; ++k) {\n                method_idx += dex::ReadULeb128(&class_data_ptr);\n                class_data.virtual_methods[k] = static_cast<jint>(method_idx);\n                class_data.virtual_methods_access_flags[k] =\n                    static_cast<jint>(dex::ReadULeb128(&class_data_ptr));\n                auto code_off = dex::ReadULeb128(&class_data_ptr);\n                class_data.virtual_methods_code[k] =\n                    code_off ? dex.dataPtr<dex::Code>(code_off) : nullptr;\n            }\n        }\n\n        // Optionally skip the expensive annotation parsing.\n        if (!include_annotations) continue;\n\n        // Parse annotations for the class, its fields, methods, and parameters.\n        ParseAnnotationSet(dex, annotation_list, array_list, class_data.annotations,\n                           class_annotation);\n\n        auto *field_annotations =\n            annotations ? reinterpret_cast<const dex::FieldAnnotationsItem *>(annotations + 1)\n                        : nullptr;\n        for (size_t k = 0; k < field_annotations_count; ++k) {\n            auto *field_annotation =\n                dex.dataPtr<dex::AnnotationSetItem>(field_annotations[k].annotations_off);\n            ParseAnnotationSet(\n                dex, annotation_list, array_list,\n                dex.field_annotations[static_cast<jint>(field_annotations[k].field_idx)],\n                field_annotation);\n        }\n\n        auto *method_annotations = field_annotations\n                                       ? reinterpret_cast<const dex::MethodAnnotationsItem *>(\n                                             field_annotations + field_annotations_count)\n                                       : nullptr;\n        for (size_t k = 0; k < method_annotations_count; ++k) {\n            auto *method_annotation =\n                dex.dataPtr<dex::AnnotationSetItem>(method_annotations[k].annotations_off);\n            ParseAnnotationSet(\n                dex, annotation_list, array_list,\n                dex.method_annotations[static_cast<jint>(method_annotations[k].method_idx)],\n                method_annotation);\n        }\n\n        auto *parameter_annotations = method_annotations\n                                          ? reinterpret_cast<const dex::ParameterAnnotationsItem *>(\n                                                method_annotations + method_annotations_count)\n                                          : nullptr;\n        for (size_t k = 0; k < parameter_annotations_count; ++k) {\n            auto *parameter_annotation =\n                dex.dataPtr<dex::AnnotationSetRefList>(parameter_annotations[k].annotations_off);\n            auto &indices =\n                dex.parameter_annotations[static_cast<jint>(parameter_annotations[k].method_idx)];\n            for (size_t l = 0; l < parameter_annotation->size; ++l) {\n                if (parameter_annotation->list[l].annotations_off != 0) {\n                    auto *parameter_annotation_item = dex.dataPtr<dex::AnnotationSetItem>(\n                        parameter_annotation->list[l].annotations_off);\n                    ParseAnnotationSet(dex, annotation_list, array_list, indices,\n                                       parameter_annotation_item);\n                }\n                // A kNoIndex entry serves as a separator between parameter annotation sets.\n                indices.emplace_back(dex::kNoIndex);\n            }\n        }\n    }\n\n    // If annotations were skipped, we are done.\n    if (!include_annotations) return out;\n\n    // 7. Convert parsed C++ annotation structures to Java objects.\n    auto out5 = env->NewIntArray(static_cast<jint>(2 * annotation_list.size()));\n    auto out6 =\n        env->NewObjectArray(static_cast<jint>(2 * annotation_list.size()), object_class, nullptr);\n    auto out5_ptr = env->GetIntArrayElements(out5, nullptr);\n    size_t i = 0;\n    for (auto &[visibility, type, items] : annotation_list) {\n        auto out6i0 = env->NewIntArray(static_cast<jint>(2 * items.size()));\n        auto out6i0_ptr = env->GetIntArrayElements(out6i0, nullptr);\n        auto out6i1 = env->NewObjectArray(static_cast<jint>(items.size()), object_class, nullptr);\n        size_t j = 0;\n        for (auto &[name, value] : items) {\n            auto &[value_type, value_data] = value;\n            // The raw value data is passed in a direct ByteBuffer.\n            auto java_value = value_data.empty()\n                                  ? nullptr\n                                  : env->NewDirectByteBuffer(value_data.data(), value_data.size());\n            env->SetObjectArrayElement(out6i1, static_cast<jint>(j), java_value);\n            out6i0_ptr[2 * j] = name;\n            out6i0_ptr[2 * j + 1] = value_type;\n            env->DeleteLocalRef(java_value);\n            ++j;\n        }\n        env->ReleaseIntArrayElements(out6i0, out6i0_ptr, 0);\n        env->SetObjectArrayElement(out6, static_cast<jint>(2 * i), out6i0);\n        env->SetObjectArrayElement(out6, static_cast<jint>(2 * i + 1), out6i1);\n        out5_ptr[2 * i] = visibility;\n        out5_ptr[2 * i + 1] = type;\n        env->DeleteLocalRef(out6i0);\n        env->DeleteLocalRef(out6i1);\n        ++i;\n    }\n    env->ReleaseIntArrayElements(out5, out5_ptr, 0);\n    env->SetObjectArrayElement(out, 5, out5);\n    env->SetObjectArrayElement(out, 6, out6);\n    env->DeleteLocalRef(out5);\n    env->DeleteLocalRef(out6);\n\n    // 8. Convert parsed C++ array values to Java objects.\n    auto out7 =\n        env->NewObjectArray(static_cast<jint>(2 * array_list.size()), object_class, nullptr);\n    i = 0;\n    for (auto &array : array_list) {\n        auto out7i0 = env->NewIntArray(static_cast<jint>(array.size()));\n        auto out7i0_ptr = env->GetIntArrayElements(out7i0, nullptr);\n        auto out7i1 = env->NewObjectArray(static_cast<jint>(array.size()), object_class, nullptr);\n        size_t j = 0;\n        for (auto &value : array) {\n            auto &[value_type, value_data] = value;\n            auto java_value = value_data.empty()\n                                  ? nullptr\n                                  : env->NewDirectByteBuffer(value_data.data(), value_data.size());\n            out7i0_ptr[j] = value_type;\n            env->SetObjectArrayElement(out7i1, static_cast<jint>(j), java_value);\n            env->DeleteLocalRef(java_value);\n            ++j;\n        }\n        env->ReleaseIntArrayElements(out7i0, out7i0_ptr, 0);\n        env->SetObjectArrayElement(out7, static_cast<jint>(2 * i), out7i0);\n        env->SetObjectArrayElement(out7, static_cast<jint>(2 * i + 1), out7i1);\n        env->DeleteLocalRef(out7i0);\n        env->DeleteLocalRef(out7i1);\n        ++i;\n    }\n    env->SetObjectArrayElement(out, 7, out7);\n    env->DeleteLocalRef(out7);\n\n    return out;\n}\n\n/**\n * @brief JNI method to release the native DexParser object.\n * @param cookie The pointer to the DexParser object created by `openDex`.\n */\nVECTOR_DEF_NATIVE_METHOD(void, DexParserBridge, closeDex, jlong cookie) {\n    if (cookie != 0) delete reinterpret_cast<DexParser *>(cookie);\n}\n\n/**\n * @brief Iterates through classes, fields, and methods, calling back to a Java\n * visitor.\n * @param cookie The pointer to the DexParser object.\n * @param visitor The main Java visitor object.\n * @param ...visitor_class/.._method Java reflection objects used to\n *        get method IDs and perform type checks.\n */\nVECTOR_DEF_NATIVE_METHOD(void, DexParserBridge, visitClass, jlong cookie, jobject visitor,\n                         jclass field_visitor_class, jclass method_visitor_class,\n                         jobject class_visit_method, jobject field_visit_method,\n                         jobject method_visit_method, jobject method_body_visit_method,\n                         jobject stop_method) {\n    // Constants for DEX opcodes used in method body parsing.\n    static constexpr dex::u1 kOpcodeMask = 0xff;\n    static constexpr dex::u1 kOpcodeNoOp = 0x00;\n    static constexpr dex::u1 kOpcodeConstString = 0x1a;\n    static constexpr dex::u1 kOpcodeConstStringJumbo = 0x1b;\n    static constexpr dex::u1 kOpcodeIGetStart = 0x52;\n    static constexpr dex::u1 kOpcodeIGetEnd = 0x58;\n    static constexpr dex::u1 kOpcodeSGetStart = 0x60;\n    static constexpr dex::u1 kOpcodeSGetEnd = 0x66;\n    static constexpr dex::u1 kOpcodeIPutStart = 0x59;\n    static constexpr dex::u1 kOpcodeIPutEnd = 0x5f;\n    static constexpr dex::u1 kOpcodeSPutStart = 0x67;\n    static constexpr dex::u1 kOpcodeSPutEnd = 0x6d;\n    static constexpr dex::u1 kOpcodeInvokeStart = 0x6e;\n    static constexpr dex::u1 kOpcodeInvokeEnd = 0x72;\n    static constexpr dex::u1 kOpcodeInvokeRangeStart = 0x74;\n    static constexpr dex::u1 kOpcodeInvokeRangeEnd = 0x78;\n    // Constants for special \"payload\" opcodes that follow a NOP instruction.\n    static constexpr dex::u2 kInstPackedSwitchPlayLoad = 0x0100;\n    static constexpr dex::u2 kInstSparseSwitchPlayLoad = 0x0200;\n    static constexpr dex::u2 kInstFillArrayDataPlayLoad = 0x0300;\n\n    if (cookie == 0) {\n        return;\n    }\n    auto &dex = *reinterpret_cast<DexParser *>(cookie);\n    // Get jmethodIDs from the reflected java.lang.reflect.Method objects.\n    auto *visit_class = env->FromReflectedMethod(class_visit_method);\n    auto *visit_field = env->FromReflectedMethod(field_visit_method);\n    auto *visit_method = env->FromReflectedMethod(method_visit_method);\n    auto *visit_method_body = env->FromReflectedMethod(method_body_visit_method);\n    auto *stop = env->FromReflectedMethod(stop_method);\n\n    auto classes = dex.ClassDefs();\n\n    for (size_t i = 0; i < classes.size(); ++i) {\n        auto &class_def = classes[i];\n        auto &class_data = dex.class_data[i];\n\n        // --- Prepare arguments for the visit_class callback ---\n        // This involves converting C++ vectors of integers into Java int arrays.\n        auto interfaces = env->NewIntArray(static_cast<jint>(class_data.interfaces.size()));\n        env->SetIntArrayRegion(interfaces, 0, static_cast<jint>(class_data.interfaces.size()),\n                               class_data.interfaces.data());\n        auto static_fields = env->NewIntArray(static_cast<jint>(class_data.static_fields.size()));\n        env->SetIntArrayRegion(static_fields, 0, static_cast<jint>(class_data.static_fields.size()),\n                               class_data.static_fields.data());\n        auto static_fields_access_flags =\n            env->NewIntArray(static_cast<jint>(class_data.static_fields_access_flags.size()));\n        env->SetIntArrayRegion(static_fields_access_flags, 0,\n                               static_cast<jint>(class_data.static_fields_access_flags.size()),\n                               class_data.static_fields_access_flags.data());\n        auto instance_fields =\n            env->NewIntArray(static_cast<jint>(class_data.instance_fields.size()));\n        env->SetIntArrayRegion(instance_fields, 0,\n                               static_cast<jint>(class_data.instance_fields.size()),\n                               class_data.instance_fields.data());\n        auto instance_fields_access_flags =\n            env->NewIntArray(static_cast<jint>(class_data.instance_fields_access_flags.size()));\n        env->SetIntArrayRegion(instance_fields_access_flags, 0,\n                               static_cast<jint>(class_data.instance_fields_access_flags.size()),\n                               class_data.instance_fields_access_flags.data());\n        auto direct_methods = env->NewIntArray(static_cast<jint>(class_data.direct_methods.size()));\n        env->SetIntArrayRegion(direct_methods, 0,\n                               static_cast<jint>(class_data.direct_methods.size()),\n                               class_data.direct_methods.data());\n        auto direct_methods_access_flags =\n            env->NewIntArray(static_cast<jint>(class_data.direct_methods_access_flags.size()));\n        env->SetIntArrayRegion(direct_methods_access_flags, 0,\n                               static_cast<jint>(class_data.direct_methods_access_flags.size()),\n                               class_data.direct_methods_access_flags.data());\n        auto virtual_methods =\n            env->NewIntArray(static_cast<jint>(class_data.virtual_methods.size()));\n        env->SetIntArrayRegion(virtual_methods, 0,\n                               static_cast<jint>(class_data.virtual_methods.size()),\n                               class_data.virtual_methods.data());\n        auto virtual_methods_access_flags =\n            env->NewIntArray(static_cast<jint>(class_data.virtual_methods_access_flags.size()));\n        env->SetIntArrayRegion(virtual_methods_access_flags, 0,\n                               static_cast<jint>(class_data.virtual_methods_access_flags.size()),\n                               class_data.virtual_methods_access_flags.data());\n        auto class_annotations = env->NewIntArray(static_cast<jint>(class_data.annotations.size()));\n        env->SetIntArrayRegion(class_annotations, 0,\n                               static_cast<jint>(class_data.annotations.size()),\n                               class_data.annotations.data());\n\n        // --- Call back to the Java visitor for the class ---\n        jobject member_visitor = env->CallObjectMethod(\n            visitor, visit_class, static_cast<jint>(class_def.class_idx),\n            static_cast<jint>(class_def.access_flags), static_cast<jint>(class_def.superclass_idx),\n            interfaces, static_cast<jint>(class_def.source_file_idx), static_fields,\n            static_fields_access_flags, instance_fields, instance_fields_access_flags,\n            direct_methods, direct_methods_access_flags, virtual_methods,\n            virtual_methods_access_flags, class_annotations);\n\n        // --- Clean up local JNI references ---\n        env->DeleteLocalRef(interfaces);\n        env->DeleteLocalRef(static_fields);\n        env->DeleteLocalRef(static_fields_access_flags);\n        env->DeleteLocalRef(instance_fields);\n        env->DeleteLocalRef(instance_fields_access_flags);\n        env->DeleteLocalRef(direct_methods);\n        env->DeleteLocalRef(direct_methods_access_flags);\n        env->DeleteLocalRef(virtual_methods);\n        env->DeleteLocalRef(virtual_methods_access_flags);\n        env->DeleteLocalRef(class_annotations);\n\n        // --- Visit fields ---\n        if (member_visitor && env->IsInstanceOf(member_visitor, field_visitor_class)) {\n            jboolean stopped = JNI_FALSE;\n            // This structured binding provides a clean way to iterate over both\n            // static and instance field collections.\n            for (auto &[fields, fields_access_flags] :\n                 {std::tie(class_data.static_fields, class_data.static_fields_access_flags),\n                  std::tie(class_data.instance_fields, class_data.instance_fields_access_flags)}) {\n                for (size_t j = 0; j < fields.size(); j++) {\n                    auto field_idx = fields[j];\n                    auto access_flags = fields_access_flags[j];\n                    auto &field_annotations = dex.field_annotations[field_idx];\n                    auto annotations =\n                        env->NewIntArray(static_cast<jint>(field_annotations.size()));\n                    env->SetIntArrayRegion(annotations, 0,\n                                           static_cast<jint>(field_annotations.size()),\n                                           field_annotations.data());\n                    // Call back to Java for this field.\n                    env->CallVoidMethod(member_visitor, visit_field, field_idx, access_flags,\n                                        annotations);\n                    env->DeleteLocalRef(annotations);\n                    // Check if the visitor wants to stop iteration.\n                    stopped = env->CallBooleanMethod(member_visitor, stop);\n                    if (stopped == JNI_TRUE) break;\n                }\n                if (stopped == JNI_TRUE) break;\n            }\n        }\n\n        // --- Visit methods ---\n        if (member_visitor && env->IsInstanceOf(member_visitor, method_visitor_class)) {\n            jboolean stopped = JNI_FALSE;\n            // Iterate over both direct and virtual methods.\n            for (auto &[methods, methods_access_flags, methods_code] :\n                 {std::tie(class_data.direct_methods, class_data.direct_methods_access_flags,\n                           class_data.direct_methods_code),\n                  std::tie(class_data.virtual_methods, class_data.virtual_methods_access_flags,\n                           class_data.virtual_methods_code)}) {\n                for (size_t j = 0; j < methods.size(); j++) {\n                    auto method_idx = methods[j];\n                    auto access_flags = methods_access_flags[j];\n                    auto code = methods_code[j];\n                    auto method_annotation = dex.method_annotations[method_idx];\n                    auto method_annotations =\n                        env->NewIntArray(static_cast<jint>(method_annotation.size()));\n                    env->SetIntArrayRegion(method_annotations, 0,\n                                           static_cast<jint>(method_annotation.size()),\n                                           method_annotation.data());\n                    auto parameter_annotation = dex.parameter_annotations[method_idx];\n                    auto parameter_annotations =\n                        env->NewIntArray(static_cast<jint>(parameter_annotation.size()));\n                    env->SetIntArrayRegion(parameter_annotations, 0,\n                                           static_cast<jint>(parameter_annotation.size()),\n                                           parameter_annotation.data());\n                    // Call back to Java for this method.\n                    // This may return a \"body visitor\".\n                    auto body_visitor = env->CallObjectMethod(\n                        member_visitor, visit_method, method_idx, access_flags, code != nullptr,\n                        method_annotations, parameter_annotations);\n                    env->DeleteLocalRef(method_annotations);\n                    env->DeleteLocalRef(parameter_annotations);\n                    // --- Lazily parse the method body if requested ---\n                    if (body_visitor && code != nullptr) {\n                        auto &body = dex.method_bodies[method_idx];\n                        if (!body.loaded) {\n                            // Using hash sets for efficient collection of unique indices.\n                            phmap::flat_hash_set<jint> referred_strings;\n                            phmap::flat_hash_set<jint> assigned_fields;\n                            phmap::flat_hash_set<jint> accessed_fields;\n                            phmap::flat_hash_set<jint> invoked_methods;\n\n                            const dex::u2 *inst = code->insns;\n                            const dex::u2 *end = inst + code->insns_size;\n                            // Iterate through the bytecode instructions.\n                            while (inst < end) {\n                                dex::u1 opcode = *inst & kOpcodeMask;\n                                body.opcodes.push_back(static_cast<jbyte>(opcode));\n                                // Check for opcodes of interest.\n                                if (opcode == kOpcodeConstString) {\n                                    auto str_idx = inst[1];\n                                    referred_strings.emplace(str_idx);\n                                }\n                                if (opcode == kOpcodeConstStringJumbo) {\n                                    auto str_idx = *reinterpret_cast<const dex::u4 *>(&inst[1]);\n                                    referred_strings.emplace(static_cast<jint>(str_idx));\n                                }\n                                if ((opcode >= kOpcodeIGetStart && opcode <= kOpcodeIGetEnd) ||\n                                    (opcode >= kOpcodeSGetStart && opcode <= kOpcodeSGetEnd)) {\n                                    auto field_idx = inst[1];\n                                    accessed_fields.emplace(field_idx);\n                                }\n                                if ((opcode >= kOpcodeIPutStart && opcode <= kOpcodeIPutEnd) ||\n                                    (opcode >= kOpcodeSPutStart && opcode <= kOpcodeSPutEnd)) {\n                                    auto field_idx = inst[1];\n                                    assigned_fields.emplace(field_idx);\n                                }\n                                if ((opcode >= kOpcodeInvokeStart && opcode <= kOpcodeInvokeEnd) ||\n                                    (opcode >= kOpcodeInvokeRangeStart &&\n                                     opcode <= kOpcodeInvokeRangeEnd)) {\n                                    auto callee = inst[1];\n                                    invoked_methods.emplace(callee);\n                                }\n                                // Handle special payload instructions which have variable\n                                // length.\n                                if (opcode == kOpcodeNoOp) {\n                                    if (*inst == kInstPackedSwitchPlayLoad) {\n                                        inst += inst[1] * 2 + 3;\n                                    } else if (*inst == kInstSparseSwitchPlayLoad) {\n                                        inst += inst[1] * 4 + 1;\n                                    } else if (*inst == kInstFillArrayDataPlayLoad) {\n                                        inst += (*reinterpret_cast<const dex::u4 *>(&inst[2]) *\n                                                     inst[1] +\n                                                 1) /\n                                                    2 +\n                                                3;\n                                    }\n                                }\n                                // Advance instruction pointer by the known length of\n                                // the current opcode.\n                                inst += dex::opcode_len[opcode];\n                            }\n                            // Copy the collected unique indices into the body's vectors.\n                            body.referred_strings.assign(referred_strings.begin(),\n                                                         referred_strings.end());\n                            body.assigned_fields.assign(assigned_fields.begin(),\n                                                        assigned_fields.end());\n                            body.accessed_fields.assign(accessed_fields.begin(),\n                                                        accessed_fields.end());\n                            body.invoked_methods.assign(invoked_methods.begin(),\n                                                        invoked_methods.end());\n                            body.loaded = true;\n                        }\n                        // --- Prepare arguments and call back for the method body ---\n                        auto referred_strings =\n                            env->NewIntArray(static_cast<jint>(body.referred_strings.size()));\n                        env->SetIntArrayRegion(referred_strings, 0,\n                                               static_cast<jint>(body.referred_strings.size()),\n                                               body.referred_strings.data());\n                        auto accessed_fields =\n                            env->NewIntArray(static_cast<jint>(body.accessed_fields.size()));\n                        env->SetIntArrayRegion(accessed_fields, 0,\n                                               static_cast<jint>(body.accessed_fields.size()),\n                                               body.accessed_fields.data());\n                        auto assigned_fields =\n                            env->NewIntArray(static_cast<jint>(body.assigned_fields.size()));\n                        env->SetIntArrayRegion(assigned_fields, 0,\n                                               static_cast<jint>(body.assigned_fields.size()),\n                                               body.assigned_fields.data());\n                        auto invoked_methods =\n                            env->NewIntArray(static_cast<jint>(body.invoked_methods.size()));\n                        env->SetIntArrayRegion(invoked_methods, 0,\n                                               static_cast<jint>(body.invoked_methods.size()),\n                                               body.invoked_methods.data());\n                        auto opcodes = env->NewByteArray(static_cast<jint>(body.opcodes.size()));\n                        env->SetByteArrayRegion(opcodes, 0, static_cast<jint>(body.opcodes.size()),\n                                                body.opcodes.data());\n                        env->CallVoidMethod(body_visitor, visit_method_body, referred_strings,\n                                            invoked_methods, accessed_fields, assigned_fields,\n                                            opcodes);\n                    }\n                    stopped = env->CallBooleanMethod(member_visitor, stop);\n                    if (stopped == JNI_TRUE) break;\n                }\n                if (stopped == JNI_TRUE) break;\n            }\n        }\n        // Check if the top-level visitor wants to stop.\n        if (env->CallBooleanMethod(visitor, stop) == JNI_TRUE) break;\n    }\n}\n\n// Array of native method descriptors for JNI registration.\nstatic JNINativeMethod gMethods[] = {\n    VECTOR_NATIVE_METHOD(DexParserBridge, openDex, \"(Ljava/nio/ByteBuffer;[J)Ljava/lang/Object;\"),\n    VECTOR_NATIVE_METHOD(DexParserBridge, closeDex, \"(J)V\"),\n    VECTOR_NATIVE_METHOD(DexParserBridge, visitClass,\n                         \"(JLjava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/\"\n                         \"reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/\"\n                         \"Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V\"),\n};\n\n/**\n * @brief Registers the native methods with the JVM.\n */\nvoid RegisterDexParserBridge(JNIEnv *env) { REGISTER_VECTOR_NATIVE_METHODS(DexParserBridge); }\n\n}  // namespace vector::native::jni\n"
  },
  {
    "path": "native/src/jni/hook_bridge.cpp",
    "content": "#include <alloca.h>\n#include <parallel_hashmap/phmap.h>\n\n#include <lsplant.hpp>\n#include <memory>\n#include <shared_mutex>\n\n#include \"jni/jni_bridge.h\"\n#include \"jni/jni_hooks.h\"\n\nnamespace {\n/**\n * @struct ModuleCallback\n * @brief Stores the jmethodIDs for the \"modern\" callback API.\n * This API separates the logic that runs before and after the original method.\n */\nstruct ModuleCallback {\n    jmethodID before_method;\n    jmethodID after_method;\n};\n\n/**\n * @struct HookItem\n * @brief Holds all state associated with a single hooked method.\n *\n * This includes lists of all registered callback functions\n * (both modern and legacy), sorted by priority.\n *\n * It also manages a thread-safe \"backup\" object,\n * which is a handle to the original, un-hooked method.\n */\nstruct HookItem {\n    // Callbacks are stored in multimaps, keyed by priority.\n    // std::greater<> ensures that higher priority numbers are processed first.\n    std::multimap<jint, jobject, std::greater<>> legacy_callbacks;\n    std::multimap<jint, ModuleCallback, std::greater<>> modern_callbacks;\n\nprivate:\n    // The backup is an atomic jobject.\n    // This is crucial for thread safety during the initial hooking process.\n    // It can be in one of three states:\n    // - nullptr: The hook has not been initialized yet.\n    // - FAILED: The hook attempt failed.\n    // - A valid jobject: A handle to the original method.\n    std::atomic<jobject> backup{nullptr};\n    static_assert(decltype(backup)::is_always_lock_free);\n    // A sentinel value to indicate that the hooking process failed.\n    inline static jobject FAILED = reinterpret_cast<jobject>(std::numeric_limits<uintptr_t>::max());\n\npublic:\n    /**\n     * @brief Atomically and safely retrieves the backup method handle.\n     * If another thread is currently setting up the hook, this method will wait until\n     * the process is complete, to prevent race conditions.\n     */\n    jobject GetBackup() {\n        // Wait until the 'backup' atomic is no longer nullptr.\n        backup.wait(nullptr, std::memory_order_acquire);\n        if (auto bk = backup.load(std::memory_order_relaxed); bk != FAILED) {\n            return bk;\n        } else {\n            return nullptr;\n        }\n    }\n\n    /**\n     * @brief Atomically sets the backup method handle once after hooking.\n     * This method uses compare_exchange_strong to ensure it only sets the value once.\n     * After setting, it notifies any waiting threads.\n     */\n    void SetBackup(jobject newBackup) {\n        jobject null = nullptr;\n        // Attempt to transition from nullptr to the new backup (or FAILED).\n        // memory_order_acq_rel ensures memory synchronization\n        // with both waiting threads (acquire) and subsequent reads (release).\n        backup.compare_exchange_strong(null, newBackup ? newBackup : FAILED,\n                                       std::memory_order_acq_rel, std::memory_order_relaxed);\n        // Wake up all threads that were waiting in GetBackup().\n        backup.notify_all();\n    }\n};\n\n// A type alias for a thread-safe parallel hash map.\n// This map is the central registry, mapping a method's ID to its HookItem.\n// It uses a std::shared_mutex to allow concurrent reads but exclusive writes.\ntemplate <class K, class V, class Hash = phmap::priv::hash_default_hash<K>,\n          class Eq = phmap::priv::hash_default_eq<K>,\n          class Alloc = phmap::priv::Allocator<phmap::priv::Pair<const K, V>>, size_t N = 4>\nusing SharedHashMap = phmap::parallel_flat_hash_map<K, V, Hash, Eq, Alloc, N, std::shared_mutex>;\n\n// The global map of all hooked methods.\nSharedHashMap<jmethodID, std::unique_ptr<HookItem>> hooked_methods;\n\n// Cached JNI method and field IDs for performance.\n// Looking these up frequently is slow, so they are cached on first use.\njmethodID invoke = nullptr;\njmethodID callback_ctor = nullptr;\njfieldID before_method_field = nullptr;\njfieldID after_method_field = nullptr;\n}  // namespace\n\nnamespace vector::native::jni {\n/**\n * @brief JNI method to install a hook on a given method or constructor.\n * @param useModernApi Distinguishes between the legacy and modern callback\n * types.\n * @param hookMethod The java.lang.reflect.Executable to be hooked.\n * @param hooker The Java class that acts as the hook trampoline.\n * @param priority The priority of this callback.\n * @param callback The Java callback object.\n * @return JNI_TRUE on success, JNI_FALSE on failure.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jboolean useModernApi,\n                         jobject hookMethod, jclass hooker, jint priority, jobject callback) {\n    bool newHook = false;\n\n#ifndef NDEBUG\n    // Simple RAII struct for performance timing in debug builds.\n    struct finally {\n        std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();\n        bool &newHook;\n        ~finally() {\n            auto finish = std::chrono::steady_clock::now();\n            if (newHook) {\n                LOGV(\"New hook took {}us\",\n                     std::chrono::duration_cast<std::chrono::microseconds>(finish - start).count());\n            }\n        }\n    } finally{.newHook = newHook};\n#endif\n\n    auto target = env->FromReflectedMethod(hookMethod);\n    HookItem *hook_item = nullptr;\n\n    // Atomically find or create an entry for the target method.\n    // This is a highly concurrent operation.\n    hooked_methods.lazy_emplace_l(\n        target,\n        // Lambda for existing element: just get the pointer.\n        [&hook_item](auto &it) { hook_item = it.second.get(); },\n        // Lambda for new element: create the HookItem and mark it as a new hook.\n        [&hook_item, &target, &newHook](const auto &ctor) {\n            auto ptr = std::make_unique<HookItem>();\n            hook_item = ptr.get();\n            ctor(target, std::move(ptr));\n            newHook = true;\n        });\n\n    // If this is the first time this method is being hooked,\n    // we need to perform the actual native hook using lsplant.\n    if (newHook) {\n        auto init = env->GetMethodID(hooker, \"<init>\", \"(Ljava/lang/reflect/Executable;)V\");\n        auto callback_method = env->ToReflectedMethod(\n            hooker, env->GetMethodID(hooker, \"callback\", \"([Ljava/lang/Object;)Ljava/lang/Object;\"),\n            false);\n        auto hooker_object = env->NewObject(hooker, init, hookMethod);\n        // Use lsplant to replace the target method with our trampoline.\n        // The returned jobject is a handle to the original method.\n        hook_item->SetBackup(lsplant::Hook(env, hookMethod, hooker_object, callback_method));\n        env->DeleteLocalRef(hooker_object);\n    }\n\n    // Wait for the backup to become available (it might be set by another thread).\n    jobject backup = hook_item->GetBackup();\n    if (!backup) return JNI_FALSE;\n\n    // Use an RAII monitor to lock the backup object,\n    // ensuring thread-safe modification of the callback lists.\n    lsplant::JNIMonitor monitor(env, backup);\n\n    if (useModernApi) {\n        // Lazy initialization of JNI IDs for the modern API.\n        if (before_method_field == nullptr) {\n            auto callback_class = env->GetObjectClass(callback);\n            callback_ctor =\n                env->GetMethodID(callback_class, \"<init>\",\n                                 \"(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V\");\n            before_method_field =\n                env->GetFieldID(callback_class, \"beforeInvocation\", \"Ljava/lang/reflect/Method;\");\n            after_method_field =\n                env->GetFieldID(callback_class, \"afterInvocation\", \"Ljava/lang/reflect/Method;\");\n        }\n        // Extract the before/after methods from the Java callback object.\n        auto before_method = env->GetObjectField(callback, before_method_field);\n        auto after_method = env->GetObjectField(callback, after_method_field);\n        auto callback_type = ModuleCallback{\n            .before_method = env->FromReflectedMethod(before_method),\n            .after_method = env->FromReflectedMethod(after_method),\n        };\n        hook_item->modern_callbacks.emplace(priority, callback_type);\n    } else {\n        // For the legacy API, store a global reference to the callback object itself.\n        hook_item->legacy_callbacks.emplace(priority, env->NewGlobalRef(callback));\n    }\n    return JNI_TRUE;\n}\n\n/**\n * @brief JNI method to remove a previously installed hook callback.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jboolean useModernApi,\n                         jobject hookMethod, jobject callback) {\n    auto target = env->FromReflectedMethod(hookMethod);\n    HookItem *hook_item = nullptr;\n    // Find the HookItem for the target method.\n    hooked_methods.if_contains(target,\n                               [&hook_item](const auto &it) { hook_item = it.second.get(); });\n    if (!hook_item) return JNI_FALSE;\n\n    jobject backup = hook_item->GetBackup();\n    if (!backup) return JNI_FALSE;\n\n    // Lock to safely modify the callback list.\n    lsplant::JNIMonitor monitor(env, backup);\n\n    if (useModernApi) {\n        auto before_method = env->GetObjectField(callback, before_method_field);\n        auto before = env->FromReflectedMethod(before_method);\n        // Find the callback by comparing the before_method's ID.\n        for (auto i = hook_item->modern_callbacks.begin(); i != hook_item->modern_callbacks.end();\n             ++i) {\n            if (before == i->second.before_method) {\n                hook_item->modern_callbacks.erase(i);\n                return JNI_TRUE;\n            }\n        }\n    } else {\n        // Find the callback by comparing the jobject directly.\n        for (auto i = hook_item->legacy_callbacks.begin(); i != hook_item->legacy_callbacks.end();\n             ++i) {\n            if (env->IsSameObject(i->second, callback)) {\n                env->DeleteGlobalRef(i->second);  // Clean up the global reference.\n                hook_item->legacy_callbacks.erase(i);\n                return JNI_TRUE;\n            }\n        }\n    }\n    return JNI_FALSE;\n}\n\n/**\n * @brief JNI method to request de-optimization of a method.\n * This can be necessary for some types of hooks to work correctly on JIT-compiled methods.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, HookBridge, deoptimizeMethod, jobject hookMethod) {\n    return lsplant::Deoptimize(env, hookMethod);\n}\n\n/**\n * @brief JNI method to invoke the original, un-hooked method.\n */\nVECTOR_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalMethod, jobject hookMethod,\n                         jobject thiz, jobjectArray args) {\n    auto target = env->FromReflectedMethod(hookMethod);\n    HookItem *hook_item = nullptr;\n    hooked_methods.if_contains(target,\n                               [&hook_item](const auto &it) { hook_item = it.second.get(); });\n\n    // If a hook item exists, invoke its backup. Otherwise, invoke the method directly\n    // (though this case should be rare if called from a hook callback).\n    jobject method_to_invoke = hook_item ? hook_item->GetBackup() : hookMethod;\n    if (!method_to_invoke) {\n        // Hooking might have failed or is not complete.\n        return nullptr;\n    }\n    return env->CallObjectMethod(method_to_invoke, invoke, thiz, args);\n}\n\n/**\n * @brief JNI wrapper around AllocObject.\n */\nVECTOR_DEF_NATIVE_METHOD(jobject, HookBridge, allocateObject, jclass cls) {\n    return env->AllocObject(cls);\n}\n\n/**\n * Core JNI backend for non-virtual method invocation and special object initialization.\n *\n * Implementation details:\n * 1. Dispatches using JNI CallNonvirtual<Type>MethodA.\n * 2. Employs stack allocation (alloca) for JNI argument mapping.\n * 3. Safely mirrors standard Java reflection (NPEs on null primitives/receivers).\n * 4. Prevents JNI Type Confusion and memory leaks by caching primitive wrappers globally,\n *    while leveraging java.lang.Number for fast implicit widening/narrowing.\n * 5. Accurately catches and wraps target method exceptions into InvocationTargetException.\n */\nVECTOR_DEF_NATIVE_METHOD(jobject, HookBridge, invokeSpecialMethod, jobject method,\n                         jcharArray shorty, jclass cls, jobject thiz, jobjectArray args) {\n    // --- JNI Global Reference Caching ---\n    // Cached once per process lifecycle to maintain extreme performance and prevent JNI aborts.\n    static jclass cls_Number = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Number\"));\n    static jclass cls_Boolean = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Boolean\"));\n    static jclass cls_Character = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Character\"));\n\n    // Globally cache primitive wrapper classes for safe return value boxing\n    static jclass cls_Integer = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Integer\"));\n    static jclass cls_Double = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Double\"));\n    static jclass cls_Long = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Long\"));\n    static jclass cls_Float = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Float\"));\n    static jclass cls_Short = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Short\"));\n    static jclass cls_Byte = (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/Byte\"));\n\n    static jclass cls_ITE =\n        (jclass)env->NewGlobalRef(env->FindClass(\"java/lang/reflect/InvocationTargetException\"));\n\n    static auto *const ctor_ite = env->GetMethodID(cls_ITE, \"<init>\", \"(Ljava/lang/Throwable;)V\");\n\n    static auto *const get_int = env->GetMethodID(cls_Number, \"intValue\", \"()I\");\n    static auto *const get_double = env->GetMethodID(cls_Number, \"doubleValue\", \"()D\");\n    static auto *const get_long = env->GetMethodID(cls_Number, \"longValue\", \"()J\");\n    static auto *const get_float = env->GetMethodID(cls_Number, \"floatValue\", \"()F\");\n    static auto *const get_short = env->GetMethodID(cls_Number, \"shortValue\", \"()S\");\n    static auto *const get_byte = env->GetMethodID(cls_Number, \"byteValue\", \"()B\");\n\n    static auto *const get_char = env->GetMethodID(cls_Character, \"charValue\", \"()C\");\n    static auto *const get_boolean = env->GetMethodID(cls_Boolean, \"booleanValue\", \"()Z\");\n\n    static auto *const set_int =\n        env->GetStaticMethodID(cls_Integer, \"valueOf\", \"(I)Ljava/lang/Integer;\");\n    static auto *const set_double =\n        env->GetStaticMethodID(cls_Double, \"valueOf\", \"(D)Ljava/lang/Double;\");\n    static auto *const set_long =\n        env->GetStaticMethodID(cls_Long, \"valueOf\", \"(J)Ljava/lang/Long;\");\n    static auto *const set_float =\n        env->GetStaticMethodID(cls_Float, \"valueOf\", \"(F)Ljava/lang/Float;\");\n    static auto *const set_short =\n        env->GetStaticMethodID(cls_Short, \"valueOf\", \"(S)Ljava/lang/Short;\");\n    static auto *const set_byte =\n        env->GetStaticMethodID(cls_Byte, \"valueOf\", \"(B)Ljava/lang/Byte;\");\n    static auto *const set_char =\n        env->GetStaticMethodID(cls_Character, \"valueOf\", \"(C)Ljava/lang/Character;\");\n    static auto *const set_boolean =\n        env->GetStaticMethodID(cls_Boolean, \"valueOf\", \"(Z)Ljava/lang/Boolean;\");\n\n    auto target = env->FromReflectedMethod(method);\n    auto param_len = env->GetArrayLength(shorty) - 1;\n\n    // --- Argument & Receiver Validation ---\n    auto args_len = args != nullptr ? env->GetArrayLength(args) : 0;\n    if (args_len != param_len) {\n        env->ThrowNew(env->FindClass(\"java/lang/IllegalArgumentException\"),\n                      \"args.length does not match parameter count\");\n        return nullptr;\n    }\n\n    if (thiz == nullptr) {\n        env->ThrowNew(env->FindClass(\"java/lang/NullPointerException\"), \"null receiver\");\n        return nullptr;\n    }\n\n    // Allocate jvalue array on the stack\n    jvalue *a = param_len > 0 ? static_cast<jvalue *>(alloca(param_len * sizeof(jvalue))) : nullptr;\n\n    auto *const shorty_char = env->GetCharArrayElements(shorty, nullptr);\n    if (shorty_char == nullptr) {\n        return nullptr;  // JVM already threw OutOfMemoryError\n    }\n\n    // RAII/Helper for clean JNI array exits\n    auto abort_and_return = [&]() {\n        env->ReleaseCharArrayElements(shorty, shorty_char, JNI_ABORT);\n        return nullptr;\n    };\n\n    // --- Safe Unboxing ---\n    for (jint i = 0; i != param_len; ++i) {\n        jobject element = env->GetObjectArrayElement(args, i);\n        if (env->ExceptionCheck()) return abort_and_return();\n\n        char type = shorty_char[i + 1];\n\n        if (element == nullptr) {\n            if (type != 'L' && type != '[') {\n                env->ThrowNew(env->FindClass(\"java/lang/IllegalArgumentException\"),\n                              \"null primitive argument\");\n                return abort_and_return();\n            }\n            a[i].l = nullptr;\n        } else {\n            if (type == 'Z') {\n                if (!env->IsInstanceOf(element, cls_Boolean)) {\n                    env->ThrowNew(env->FindClass(\"java/lang/IllegalArgumentException\"),\n                                  \"Expected Boolean\");\n                    return abort_and_return();\n                }\n                a[i].z = env->CallBooleanMethod(element, get_boolean);\n            } else if (type == 'C') {\n                if (!env->IsInstanceOf(element, cls_Character)) {\n                    env->ThrowNew(env->FindClass(\"java/lang/IllegalArgumentException\"),\n                                  \"Expected Character\");\n                    return abort_and_return();\n                }\n                a[i].c = env->CallCharMethod(element, get_char);\n            } else if (type != 'L' && type != '[') {\n                bool is_number = env->IsInstanceOf(element, cls_Number) == JNI_TRUE;\n                bool is_character =\n                    !is_number && (env->IsInstanceOf(element, cls_Character) == JNI_TRUE);\n\n                if (!is_number && !is_character) {\n                    env->ThrowNew(env->FindClass(\"java/lang/IllegalArgumentException\"),\n                                  \"Expected Number or Character\");\n                    return abort_and_return();\n                }\n\n                // If a Character is passed to a numeric parameter, extract its value for widening\n                jchar c_val = 0;\n                if (is_character) {\n                    c_val = env->CallCharMethod(element, get_char);\n                    if (env->ExceptionCheck()) return abort_and_return();\n                }\n\n                switch (type) {\n                case 'I':\n                    a[i].i = env->CallIntMethod(element, get_int);\n                    break;\n                case 'D':\n                    a[i].d = env->CallDoubleMethod(element, get_double);\n                    break;\n                case 'J':\n                    a[i].j = env->CallLongMethod(element, get_long);\n                    break;\n                case 'F':\n                    a[i].f = env->CallFloatMethod(element, get_float);\n                    break;\n                case 'S':\n                    a[i].s = env->CallShortMethod(element, get_short);\n                    break;\n                case 'B':\n                    a[i].b = env->CallByteMethod(element, get_byte);\n                    break;\n                }\n            } else {\n                a[i].l = element;\n                element =\n                    nullptr;  // Transferred ownership to jvalue array; will be freed on return\n            }\n        }\n\n        if (element) env->DeleteLocalRef(element);\n        if (env->ExceptionCheck()) return abort_and_return();\n    }\n\n    // --- Non-virtual Invocation ---\n    jvalue ret_val;\n    switch (shorty_char[0]) {\n    case 'I':\n        ret_val.i = env->CallNonvirtualIntMethodA(thiz, cls, target, a);\n        break;\n    case 'D':\n        ret_val.d = env->CallNonvirtualDoubleMethodA(thiz, cls, target, a);\n        break;\n    case 'J':\n        ret_val.j = env->CallNonvirtualLongMethodA(thiz, cls, target, a);\n        break;\n    case 'F':\n        ret_val.f = env->CallNonvirtualFloatMethodA(thiz, cls, target, a);\n        break;\n    case 'S':\n        ret_val.s = env->CallNonvirtualShortMethodA(thiz, cls, target, a);\n        break;\n    case 'B':\n        ret_val.b = env->CallNonvirtualByteMethodA(thiz, cls, target, a);\n        break;\n    case 'C':\n        ret_val.c = env->CallNonvirtualCharMethodA(thiz, cls, target, a);\n        break;\n    case 'Z':\n        ret_val.z = env->CallNonvirtualBooleanMethodA(thiz, cls, target, a);\n        break;\n    case 'L':\n        ret_val.l = env->CallNonvirtualObjectMethodA(thiz, cls, target, a);\n        break;\n    default:\n        env->CallNonvirtualVoidMethodA(thiz, cls, target, a);\n        break;\n    }\n\n    // --- Exception Wrapping ---\n    jthrowable target_exception = env->ExceptionOccurred();\n    if (target_exception) {\n        env->ExceptionClear();\n        jobject ite = env->NewObject(cls_ITE, ctor_ite, target_exception);\n        // Ensure NewObject didn't fail due to OOM before throwing\n        if (ite) {\n            env->Throw(static_cast<jthrowable>(ite));\n        }\n        return abort_and_return();\n    }\n\n    // --- Box Return Value ---\n    jobject value = nullptr;\n    switch (shorty_char[0]) {\n    case 'I':\n        value = env->CallStaticObjectMethod(cls_Integer, set_int, ret_val.i);\n        break;\n    case 'D':\n        value = env->CallStaticObjectMethod(cls_Double, set_double, ret_val.d);\n        break;\n    case 'J':\n        value = env->CallStaticObjectMethod(cls_Long, set_long, ret_val.j);\n        break;\n    case 'F':\n        value = env->CallStaticObjectMethod(cls_Float, set_float, ret_val.f);\n        break;\n    case 'S':\n        value = env->CallStaticObjectMethod(cls_Short, set_short, ret_val.s);\n        break;\n    case 'B':\n        value = env->CallStaticObjectMethod(cls_Byte, set_byte, ret_val.b);\n        break;\n    case 'C':\n        value = env->CallStaticObjectMethod(cls_Character, set_char, ret_val.c);\n        break;\n    case 'Z':\n        value = env->CallStaticObjectMethod(cls_Boolean, set_boolean, ret_val.z);\n        break;\n    case 'L':\n        value = ret_val.l;\n        break;\n    case 'V':\n        value = nullptr;\n        break;\n    }\n\n    env->ReleaseCharArrayElements(shorty, shorty_char, JNI_ABORT);\n    return value;\n}\n\n/**\n * @brief JNI wrapper around IsInstanceOf.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, HookBridge, instanceOf, jobject object, jclass expected_class) {\n    return env->IsInstanceOf(object, expected_class);\n}\n\n/**\n * @brief JNI wrapper to mark a DEX file loaded from memory as trusted.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, HookBridge, setTrusted, jobject cookie) {\n    return lsplant::MakeDexFileTrusted(env, cookie);\n}\n\n/**\n * @brief Creates a snapshot of all registered callbacks for a given method.\n * This is useful for debugging and introspection from the Java side.\n\n * @return An Object[2][] array where index 0 contains modern callbacks and\n *         index 1 contains legacy callbacks.\n */\nVECTOR_DEF_NATIVE_METHOD(jobjectArray, HookBridge, callbackSnapshot, jclass callback_class,\n                         jobject method) {\n    auto target = env->FromReflectedMethod(method);\n    HookItem *hook_item = nullptr;\n    hooked_methods.if_contains(target,\n                               [&hook_item](const auto &it) { hook_item = it.second.get(); });\n    if (!hook_item) return nullptr;\n\n    jobject backup = hook_item->GetBackup();\n    if (!backup) return nullptr;\n\n    // Lock to ensure a consistent snapshot of the callback lists.\n    lsplant::JNIMonitor monitor(env, backup);\n\n    auto res = env->NewObjectArray(2, env->FindClass(\"[Ljava/lang/Object;\"), nullptr);\n    auto modern = env->NewObjectArray((jsize)hook_item->modern_callbacks.size(),\n                                      env->FindClass(\"java/lang/Object\"), nullptr);\n    auto legacy = env->NewObjectArray((jsize)hook_item->legacy_callbacks.size(),\n                                      env->FindClass(\"java/lang/Object\"), nullptr);\n\n    jsize i = 0;\n    for (const auto &callback_pair : hook_item->modern_callbacks) {\n        // The clazz argument refers to the Java class where the native method is\n        // declared, provided by the macro VECTOR_DEF_NATIVE_METHOD.\n        auto before_method =\n            env->ToReflectedMethod(clazz, callback_pair.second.before_method, JNI_FALSE);\n        auto after_method =\n            env->ToReflectedMethod(clazz, callback_pair.second.after_method, JNI_FALSE);\n        // Re-create the Java callback object from the stored method IDs.\n        auto callback_object =\n            env->NewObject(callback_class, callback_ctor, before_method, after_method);\n        env->SetObjectArrayElement(modern, i++, callback_object);\n        // Clean up local references created during object construction.\n        env->DeleteLocalRef(before_method);\n        env->DeleteLocalRef(after_method);\n        env->DeleteLocalRef(callback_object);\n    }\n\n    i = 0;\n    for (const auto &callback_pair : hook_item->legacy_callbacks) {\n        // The legacy list already stores a global ref to the callback object.\n        env->SetObjectArrayElement(legacy, i++, callback_pair.second);\n    }\n\n    env->SetObjectArrayElement(res, 0, modern);\n    env->SetObjectArrayElement(res, 1, legacy);\n    env->DeleteLocalRef(modern);\n    env->DeleteLocalRef(legacy);\n    return res;\n}\n\n/**\n * @brief  Retrieves the static initializer (<clinit>) of a class as a Method object.\n * @param target_class The class to inspect.\n * @return A Method object for the static initializer, or null if it doesn't exist.\n */\nVECTOR_DEF_NATIVE_METHOD(jobject, HookBridge, getStaticInitializer, jclass target_class) {\n    // <clinit> is the internal name for a static initializer.\n    // Its signature is always ()V (no arguments, void return).\n    jmethodID mid = env->GetStaticMethodID(target_class, \"<clinit>\", \"()V\");\n    if (!mid) {\n        // If GetStaticMethodID fails, it throws an exception.\n        // We clear it and return null to let the Java side handle it gracefully.\n        env->ExceptionClear();\n        return nullptr;\n    }\n    // Convert the method ID to a java.lang.reflect.Method object.\n    // The last parameter must be JNI_TRUE because it's a static method.\n    return env->ToReflectedMethod(target_class, mid, JNI_TRUE);\n}\n\n// Array of native method descriptors for JNI registration.\nstatic JNINativeMethod gMethods[] = {\n    VECTOR_NATIVE_METHOD(HookBridge, hookMethod,\n                         \"(ZLjava/lang/reflect/Executable;Ljava/lang/Class;ILjava/\"\n                         \"lang/Object;)Z\"),\n    VECTOR_NATIVE_METHOD(HookBridge, unhookMethod,\n                         \"(ZLjava/lang/reflect/Executable;Ljava/lang/Object;)Z\"),\n    VECTOR_NATIVE_METHOD(HookBridge, deoptimizeMethod, \"(Ljava/lang/reflect/Executable;)Z\"),\n    VECTOR_NATIVE_METHOD(HookBridge, invokeOriginalMethod,\n                         \"(Ljava/lang/reflect/Executable;Ljava/lang/Object;[Ljava/\"\n                         \"lang/Object;)Ljava/lang/Object;\"),\n    VECTOR_NATIVE_METHOD(HookBridge, invokeSpecialMethod,\n                         \"(Ljava/lang/reflect/Executable;[CLjava/lang/Class;Ljava/\"\n                         \"lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;\"),\n    VECTOR_NATIVE_METHOD(HookBridge, allocateObject, \"(Ljava/lang/Class;)Ljava/lang/Object;\"),\n    VECTOR_NATIVE_METHOD(HookBridge, instanceOf, \"(Ljava/lang/Object;Ljava/lang/Class;)Z\"),\n    VECTOR_NATIVE_METHOD(HookBridge, setTrusted, \"(Ljava/lang/Object;)Z\"),\n    VECTOR_NATIVE_METHOD(HookBridge, callbackSnapshot,\n                         \"(Ljava/lang/Class;Ljava/lang/reflect/\"\n                         \"Executable;)[[Ljava/lang/Object;\"),\n    VECTOR_NATIVE_METHOD(HookBridge, getStaticInitializer,\n                         \"(Ljava/lang/Class;)Ljava/lang/reflect/Method;\"),\n};\n\n/**\n * @brief Registers all native methods with the JVM when the library is loaded.\n */\nvoid RegisterHookBridge(JNIEnv *env) {\n    // Cache the Method.invoke methodID for use in invokeOriginalMethod.\n    jclass method = env->FindClass(\"java/lang/reflect/Method\");\n    invoke = env->GetMethodID(method, \"invoke\",\n                              \"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;\");\n    env->DeleteLocalRef(method);\n    REGISTER_VECTOR_NATIVE_METHODS(HookBridge);\n}\n}  // namespace vector::native::jni\n"
  },
  {
    "path": "native/src/jni/native_api_bridge.cpp",
    "content": "#include \"core/native_api.h\"\n#include \"jni/jni_bridge.h\"\n#include \"jni/jni_hooks.h\"\n\nnamespace vector::native::jni {\nVECTOR_DEF_NATIVE_METHOD(void, NativeAPI, recordNativeEntrypoint, jstring jstr) {\n    lsplant::JUTFString str(env, jstr);\n    vector::native::RegisterNativeLib(str);\n}\n\nstatic JNINativeMethod gMethods[] = {\n    VECTOR_NATIVE_METHOD(NativeAPI, recordNativeEntrypoint, \"(Ljava/lang/String;)V\")};\n\nvoid RegisterNativeApiBridge(JNIEnv *env) { REGISTER_VECTOR_NATIVE_METHODS(NativeAPI); }\n\n}  // namespace vector::native::jni\n"
  },
  {
    "path": "native/src/jni/resources_hook.cpp",
    "content": "#include <dex_builder.h>\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n\n#include \"common/config.h\"\n#include \"elf/elf_image.h\"\n#include \"elf/symbol_cache.h\"\n#include \"framework/android_types.h\"\n#include \"jni/jni_bridge.h\"\n#include \"jni/jni_hooks.h\"\n\nnamespace vector::native::jni {\n\n// --- Type Aliases for Native Android Framework Functions ---\n\n// Signature for android::ResXMLParser::getAttributeNameID(int)\nusing TYPE_GET_ATTR_NAME_ID = int32_t (*)(void *, int);\n// Signature for android::ResStringPool::stringAt(int, size_t*)\nusing TYPE_STRING_AT = char16_t *(*)(const void *, int32_t, size_t *);\n// Signature for android::ResXMLParser::restart()\nusing TYPE_RESTART = void (*)(void *);\n// Signature for android::ResXMLParser::next()\nusing TYPE_NEXT = int32_t (*)(void *);\n\n// --- JNI Globals & Cached IDs ---\nstatic jclass classXResources;\nstatic jmethodID methodXResourcesTranslateAttrId;\nstatic jmethodID methodXResourcesTranslateResId;\n\n// --- Native Function Pointers ---\n// To store the memory addresses of the private Android framework functions.\nstatic TYPE_NEXT ResXMLParser_next = nullptr;\nstatic TYPE_RESTART ResXMLParser_restart = nullptr;\nstatic TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr;\n\n/**\n * @brief Constructs the class name for the XResources class at runtime.\n */\nstatic std::string GetXResourcesClassName() {\n    // Use a static local variable to ensure this lookup and string manipulation\n    // only happens once.\n    static std::string name = []() {\n        auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();\n        if (obfs_map.empty()) {\n            LOGW(\"GetXResourcesClassName: obfuscation_map is empty.\");\n        }\n        // The key is the original, unobfuscated class name prefix.\n        // The value is the new, obfuscated prefix.\n        auto it = obfs_map.find(\"android.content.res.XRes\");\n        if (it == obfs_map.end()) {\n            LOGE(\"Could not find obfuscated name for XResources.\");\n            return std::string();\n        }\n        std::string jni_name = it->second + \"ources\";\n        LOGD(\"Resolved XResources class name to: {}\", jni_name.c_str());\n        return jni_name;\n    }();\n    return name;\n}\n\n/**\n * @brief Finds and caches the addresses of private functions in libframework.so.\n *\n * It uses the ElfImage utility to parse the Android framework's shared library in memory,\n * find functions by their C++ mangled names, and\n * store their addresses in our global function pointers.\n *\n * @return True if all required symbols were found, false otherwise.\n */\nstatic bool PrepareSymbols() {\n    ElfImage fw(kFrameworkLibraryName);\n    if (!fw.IsValid()) {\n        LOGE(\"Failed to open Android framework library.\");\n        return false;\n    };\n\n    // The mangled names are specific to the compiler and architecture.\n    // This is a very fragile part of the hook.\n\n    // Find android::ResXMLParser::next()\n    if (!(ResXMLParser_next = fw.getSymbAddress<TYPE_NEXT>(\"_ZN7android12ResXMLParser4nextEv\"))) {\n        LOGE(\"Failed to find symbol: ResXMLParser::next\");\n        return false;\n    }\n    // Find android::ResXMLParser::restart()\n    if (!(ResXMLParser_restart =\n              fw.getSymbAddress<TYPE_RESTART>(\"_ZN7android12ResXMLParser7restartEv\"))) {\n        LOGE(\"Failed to find symbol: ResXMLParser::restart\");\n        return false;\n    };\n    // Find android::ResXMLParser::getAttributeNameID(unsigned int/long)\n    if (!(ResXMLParser_getAttributeNameID = fw.getSymbAddress<TYPE_GET_ATTR_NAME_ID>(\n              LP_SELECT(\"_ZNK7android12ResXMLParser18getAttributeNameIDEj\",\n                        \"_ZNK7android12ResXMLParser18getAttributeNameIDEm\")))) {\n        LOGE(\"Failed to find symbol: ResXMLParser::getAttributeNameID\");\n        return false;\n    }\n    // Initialize another part of the resource framework that we depend on.\n    return android::ResStringPool::setup(lsplant::InitInfo{\n        .art_symbol_resolver = [&](auto s) { return fw.template getSymbAddress<>(s); }});\n}\n\n/**\n * @brief JNI entry point to initialize the entire native resources hook.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {\n    const auto x_resources_class_name = GetXResourcesClassName();\n    if (x_resources_class_name.empty()) {\n        return JNI_FALSE;\n    }\n\n    if (auto classXResources_ =\n            Context::GetInstance()->FindClassFromCurrentLoader(env, x_resources_class_name)) {\n        classXResources = JNI_NewGlobalRef(env, classXResources_);\n    } else {\n        LOGE(\"Error while loading XResources class '{}'\", x_resources_class_name.c_str());\n        return JNI_FALSE;\n    }\n\n    // Dynamically build the method signature using the (possibly obfuscated) class name.\n    std::string x_resources_jni_name = \"L\" + x_resources_class_name + \";\";\n    std::replace(x_resources_jni_name.begin(), x_resources_jni_name.end(), '.', '/');\n\n    methodXResourcesTranslateResId = env->GetStaticMethodID(\n        classXResources, \"translateResId\",\n        fmt::format(\"(I{}Landroid/content/res/Resources;)I\", x_resources_jni_name).c_str());\n    if (!methodXResourcesTranslateResId) {\n        LOGE(\"Failed to find method: XResources.translateResId\");\n        return JNI_FALSE;\n    }\n\n    methodXResourcesTranslateAttrId = env->GetStaticMethodID(\n        classXResources, \"translateAttrId\",\n        fmt::format(\"(Ljava/lang/String;{})I\", x_resources_jni_name).c_str());\n    if (!methodXResourcesTranslateAttrId) {\n        LOGE(\"Failed to find method: XResources.translateAttrId\");\n        return JNI_FALSE;\n    }\n\n    if (!PrepareSymbols()) {\n        LOGE(\"Failed to prepare native symbols for resource hooking.\");\n        return JNI_FALSE;\n    }\n    return JNI_TRUE;\n}\n\n/**\n * @brief Removes the 'final' modifier from a Java class at runtime.\n * This allows the framework to create subclasses of what are normally final classes.\n */\nVECTOR_DEF_NATIVE_METHOD(jboolean, ResourcesHook, makeInheritable, jclass target_class) {\n    if (lsplant::MakeClassInheritable(env, target_class)) {\n        return JNI_TRUE;\n    }\n    return JNI_FALSE;\n}\n\n/**\n * @brief Builds a new ClassLoader in memory containing dynamically generated classes.\n *\n * This function creates a DEX file on-the-fly.\n * The DEX file contains dummy classes that inherit from key Android resource classes.\n * This allows the framework to inject its own logic by later creating classes that\n * inherit from these dummies.\n *\n * @return A new dalvik.system.InMemoryDexClassLoader instance.\n */\nVECTOR_DEF_NATIVE_METHOD(jobject, ResourcesHook, buildDummyClassLoader, jobject parent,\n                         jstring resource_super_class, jstring typed_array_super_class) {\n    using namespace startop::dex;\n\n    // Cache the class and constructor for InMemoryDexClassLoader.\n    static auto in_memory_classloader =\n        (jclass)env->NewGlobalRef(env->FindClass(\"dalvik/system/InMemoryDexClassLoader\"));\n    static jmethodID initMid = env->GetMethodID(in_memory_classloader, \"<init>\",\n                                                \"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V\");\n\n    DexBuilder dex_file;\n\n    // Create a class named \"xposed.dummy.XResourcesSuperClass\".\n    ClassBuilder xresource_builder{dex_file.MakeClass(\"xposed/dummy/XResourcesSuperClass\")};\n    // Set its superclass to the one specified by the Java caller.\n    xresource_builder.setSuperClass(\n        TypeDescriptor::FromClassname(lsplant::JUTFString(env, resource_super_class).get()));\n\n    // Create a class named \"xposed.dummy.XTypedArraySuperClass\".\n    ClassBuilder xtypearray_builder{dex_file.MakeClass(\"xposed/dummy/XTypedArraySuperClass\")};\n    // Set its superclass.\n    xtypearray_builder.setSuperClass(\n        TypeDescriptor::FromClassname(lsplant::JUTFString(env, typed_array_super_class).get()));\n\n    // Finalize the DEX file into a memory buffer.\n    slicer::MemView image{dex_file.CreateImage()};\n\n    // Wrap the memory buffer in a Java ByteBuffer.\n    auto dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());\n\n    // Create and return a new InMemoryDexClassLoader instance.\n    return env->NewObject(in_memory_classloader, initMid, dex_buffer, parent);\n}\n\n/**\n * @brief The core resource rewriting function.\n *\n * This method iterates through a binary XML file as it's being parsed by the Android framework.\n * For each attribute and value, it calls back to Java to see\n * if the resource ID should be replaced with a different one.\n *\n * @param parserPtr A raw pointer to the native android::ResXMLParser object.\n * @param origRes The original XResources object.\n * @param repRes The replacement Resources object.\n */\nVECTOR_DEF_NATIVE_METHOD(void, ResourcesHook, rewriteXmlReferencesNative, jlong parserPtr,\n                         jobject origRes, jobject repRes) {\n    // Cast the long from Java back to a native C++ pointer.\n    // This is dangerous and assumes the Java code provides a valid pointer.\n    auto parser = (android::ResXMLParser *)parserPtr;\n\n    if (parser == nullptr) return;\n\n    const android::ResXMLTree &mTree = parser->mTree;\n    auto mResIds = (uint32_t *)mTree.mResIds;\n    android::ResXMLTree_attrExt *tag;\n    int attrCount;\n\n    // This loop iterates through all tokens in the binary XML file.\n    do {\n        // Call the native android::ResXMLParser::next() function via our pointer.\n        switch (ResXMLParser_next(parser)) {\n        case android::ResXMLParser::START_TAG:\n            tag = (android::ResXMLTree_attrExt *)parser->mCurExt;\n            attrCount = tag->attributeCount;\n            // Loop through all attributes of the current XML tag.\n            for (int idx = 0; idx < attrCount; idx++) {\n                auto attr =\n                    (android::ResXMLTree_attribute *)(((const uint8_t *)tag) + tag->attributeStart +\n                                                      tag->attributeSize * idx);\n\n                // Translate the attribute name's resource ID ---\n                // e.g., for 'android:textColor', translate the ID for 'textColor'.\n                int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx);\n\n                // Only replace IDs that belong to the app's package (0x7f...).\n                if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds &&\n                    mResIds[attrNameID] >= 0x7f000000) {\n                    auto attrName = mTree.mStrings.stringAt(attrNameID);\n                    jstring attrNameStr =\n                        env->NewString((const jchar *)attrName.data_, attrName.length_);\n                    if (env->ExceptionCheck()) goto leave;  // Critical check\n\n                    // Call back to Java: XResources.translateAttrId(String name, ...)\n                    jint attrResID = env->CallStaticIntMethod(\n                        classXResources, methodXResourcesTranslateAttrId, attrNameStr, origRes);\n                    env->DeleteLocalRef(attrNameStr);\n                    if (env->ExceptionCheck()) goto leave;\n\n                    // Directly modify the resource ID table in the parser's memory.\n                    mResIds[attrNameID] = attrResID;\n                }\n\n                // Translate the attribute's value if it's a reference ---\n                // e.g., for 'android:textColor=\"@color/my_text\"', translate the ID for\n                // '@color/my_text'.\n                if (attr->typedValue.dataType != android::Res_value::TYPE_REFERENCE) continue;\n\n                jint oldValue = attr->typedValue.data;\n                if (oldValue < 0x7f000000) continue;\n\n                // Call back to Java: XResources.translateResId(int id, ...)\n                jint newValue = env->CallStaticIntMethod(\n                    classXResources, methodXResourcesTranslateResId, oldValue, origRes, repRes);\n                if (env->ExceptionCheck()) goto leave;\n\n                // If the ID was changed, update the value directly in the parser's\n                // memory.\n                if (newValue != oldValue) attr->typedValue.data = newValue;\n            }\n            continue;\n        case android::ResXMLParser::END_DOCUMENT:\n        case android::ResXMLParser::BAD_DOCUMENT:\n            goto leave;  // Exit the loop.\n        default:\n            continue;  // Process next XML token.\n        }\n    } while (true);\n\n// A single exit point for the function.\nleave:\n    // Reset the parser to its initial state so it can be read again.\n    ResXMLParser_restart(parser);\n}\n\n// JNI method registration table.\nstatic JNINativeMethod gMethods[] = {\n    VECTOR_NATIVE_METHOD(ResourcesHook, initXResourcesNative, \"()Z\"),\n    VECTOR_NATIVE_METHOD(ResourcesHook, makeInheritable, \"(Ljava/lang/Class;)Z\"),\n    VECTOR_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader,\n                         \"(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/\"\n                         \"String;)Ljava/lang/ClassLoader;\"),\n    VECTOR_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative,\n                         \"(JLxposed/dummy/XResourcesSuperClass;Landroid/content/res/Resources;)V\")};\n\nvoid RegisterResourcesHook(JNIEnv *env) { REGISTER_VECTOR_NATIVE_METHODS(ResourcesHook); }\n}  // namespace vector::native::jni\n"
  },
  {
    "path": "services/daemon-service/.gitignore",
    "content": "/build"
  },
  {
    "path": "services/daemon-service/build.gradle.kts",
    "content": "plugins { alias(libs.plugins.agp.lib) }\n\nandroid {\n    buildFeatures { aidl = true }\n\n    buildTypes { release { isMinifyEnabled = false } }\n\n    sourceSets {\n        named(\"main\") {\n            java.srcDirs(\"src/main/java\", \"../libxposed/service/src/main\")\n            aidl.srcDirs(\"src/main/aidl\", \"../libxposed/interface/src/main/aidl\")\n        }\n    }\n\n    aidlPackagedList += \"org/lsposed/lspd/models/Module.aidl\"\n    namespace = \"org.lsposed.lspd.daemonservice\"\n}\n\ndependencies {\n    compileOnly(libs.androidx.annotation)\n    compileOnly(projects.hiddenapi.stubs)\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/models/Module.aidl",
    "content": "package org.lsposed.lspd.models;\nimport org.lsposed.lspd.models.PreLoadedApk;\nimport org.lsposed.lspd.service.ILSPInjectedModuleService;\n\nparcelable Module {\n    String packageName;\n    int appId;\n    String apkPath;\n    PreLoadedApk file;\n    ApplicationInfo applicationInfo;\n    ILSPInjectedModuleService service;\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl",
    "content": "package org.lsposed.lspd.models;\n\nparcelable PreLoadedApk {\n    List<SharedMemory> preLoadedDexes;\n    List<String> moduleClassNames;\n    List<String> moduleLibraryNames;\n    boolean legacy;\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl",
    "content": "package org.lsposed.lspd.service;\n\nimport org.lsposed.lspd.models.Module;\n\ninterface ILSPApplicationService {\n    boolean isLogMuted();\n\n    List<Module> getLegacyModulesList();\n\n    List<Module> getModulesList();\n\n    String getPrefsPath(String packageName);\n\n    ParcelFileDescriptor requestInjectedManagerBinder(out List<IBinder> binder);\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPInjectedModuleService.aidl",
    "content": "package org.lsposed.lspd.service;\n\nimport org.lsposed.lspd.service.IRemotePreferenceCallback;\n\ninterface ILSPInjectedModuleService {\n    int getFrameworkPrivilege();\n\n    Bundle requestRemotePreferences(String group, IRemotePreferenceCallback callback);\n\n    ParcelFileDescriptor openRemoteFile(String path);\n\n    String[] getRemoteFileList();\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPSystemServerService.aidl",
    "content": "package org.lsposed.lspd.service;\n\nimport org.lsposed.lspd.service.ILSPApplicationService;\n\ninterface ILSPSystemServerService {\n    ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat);\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPosedService.aidl",
    "content": "package org.lsposed.lspd.service;\n\nimport org.lsposed.lspd.service.ILSPApplicationService;\n\ninterface ILSPosedService {\n    ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat);\n\n    oneway void dispatchSystemServerContext(in IBinder activityThread, in IBinder activityToken, String api);\n\n    boolean preStartManager();\n\n    boolean setManagerEnabled(boolean enabled);\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/aidl/org/lsposed/lspd/service/IRemotePreferenceCallback.aidl",
    "content": "package org.lsposed.lspd.service;\n\ninterface IRemotePreferenceCallback {\n    oneway void onUpdate(in Bundle map);\n}\n"
  },
  {
    "path": "services/daemon-service/src/main/java/org/lsposed/lspd/util/Utils.java",
    "content": "/*\n * This file is part of LSPosed.\n *\n * LSPosed 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 * LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n *\n * Copyright (C) 2020 EdXposed Contributors\n * Copyright (C) 2021 LSPosed Contributors\n */\n\npackage org.lsposed.lspd.util;\n\nimport android.os.SystemProperties;\nimport android.text.TextUtils;\n\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.zone.ZoneRulesException;\n\npublic class Utils {\n\n    public static final String LOG_TAG = \"LSPosed\";\n    public static final boolean isMIUI = !TextUtils.isEmpty(SystemProperties.get(\"ro.miui.ui.version.name\"));\n    public static final boolean isLENOVO = !TextUtils.isEmpty(SystemProperties.get(\"ro.lenovo.region\"));\n\n    public class Log {\n        public static boolean muted = false;\n\n        public static String getStackTraceString(Throwable tr) {\n            return android.util.Log.getStackTraceString(tr);\n        }\n\n        public static void d(String tag, String msg) {\n            if (muted) return;\n            android.util.Log.d(tag, msg);\n        }\n\n        public static void d(String tag, String msg, Throwable tr) {\n            android.util.Log.d(tag, msg, tr);\n        }\n\n        public static void v(String tag, String msg) {\n            if (muted) return;\n            android.util.Log.v(tag, msg);\n        }\n\n        public static void v(String tag, String msg, Throwable tr) {\n            android.util.Log.v(tag, msg, tr);\n        }\n\n        public static void i(String tag, String msg) {\n            if (muted) return;\n            android.util.Log.i(tag, msg);\n        }\n\n        public static void i(String tag, String msg, Throwable tr) {\n            android.util.Log.i(tag, msg, tr);\n        }\n\n        public static void w(String tag, String msg) {\n            if (muted) return;\n            android.util.Log.w(tag, msg);\n        }\n\n        public static void w(String tag, String msg, Throwable tr) {\n            if (muted) return;\n            android.util.Log.w(tag, msg, tr);\n        }\n\n        public static void e(String tag, String msg) {\n            android.util.Log.e(tag, msg);\n        }\n\n        public static void e(String tag, String msg, Throwable tr) {\n            android.util.Log.e(tag, msg, tr);\n        }\n\n\n    }\n\n    public static void logD(Object msg) {\n        Log.d(LOG_TAG, msg.toString());\n    }\n\n    public static void logD(String msg, Throwable throwable) {\n        Log.d(LOG_TAG, msg, throwable);\n    }\n\n    public static void logW(String msg) {\n        Log.w(LOG_TAG, msg);\n    }\n\n    public static void logW(String msg, Throwable throwable) {\n        Log.w(LOG_TAG, msg, throwable);\n    }\n\n    public static void logI(String msg) {\n        Log.i(LOG_TAG, msg);\n    }\n\n    public static void logI(String msg, Throwable throwable) {\n        Log.i(LOG_TAG, msg, throwable);\n    }\n\n    public static void logE(String msg) {\n        Log.e(LOG_TAG, msg);\n    }\n\n    public static void logE(String msg, Throwable throwable) {\n        Log.e(LOG_TAG, msg, throwable);\n    }\n\n    public static ZoneId getZoneId() {\n        var timezone = SystemProperties.get(\"persist.sys.timezone\", \"GMT\");\n        try {\n            return ZoneId.of(timezone);\n        } catch (ZoneRulesException e) {\n            return ZoneOffset.UTC;\n        }\n    }\n}\n"
  },
  {
    "path": "services/manager-service/.gitignore",
    "content": "/build"
  },
  {
    "path": "services/manager-service/build.gradle.kts",
    "content": "plugins { alias(libs.plugins.agp.lib) }\n\nandroid {\n    buildFeatures { aidl = true }\n\n    buildTypes { release { isMinifyEnabled = false } }\n\n    namespace = \"org.lsposed.lspd.managerservice\"\n}\n\ndependencies { api(libs.rikkax.parcelablelist) }\n"
  },
  {
    "path": "services/manager-service/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed 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  ~ LSPosed 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 LSPosed.  If not, see <https://www.gnu.org/licenses/>.\n  ~\n  ~ Copyright (C) 2021 LSPosed Contributors\n  -->\n\n<manifest />\n"
  },
  {
    "path": "services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl",
    "content": "package org.lsposed.lspd;\n\nimport rikka.parcelablelist.ParcelableListSlice;\nimport org.lsposed.lspd.models.UserInfo;\nimport org.lsposed.lspd.models.Application;\n\n\ninterface ILSPManagerService {\n    const int DEX2OAT_OK = 0;\n    const int DEX2OAT_CRASHED = 1;\n    const int DEX2OAT_MOUNT_FAILED = 2;\n    const int DEX2OAT_SELINUX_PERMISSIVE = 3;\n    const int DEX2OAT_SEPOLICY_INCORRECT = 4;\n\n    String getApi() = 1;\n\n    ParcelableListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags, boolean filterNoProcess) = 2;\n\n    String[] enabledModules() = 3;\n\n    boolean enableModule(String packageName) = 4;\n\n    boolean disableModule(String packageName) = 5;\n\n    boolean setModuleScope(String packageName, in List<Application> scope) = 6;\n\n    List<Application> getModuleScope(String packageName) = 7;\n\n    boolean isVerboseLog() = 11;\n\n    void setVerboseLog(boolean enabled) = 12;\n\n    ParcelFileDescriptor getVerboseLog() = 16;\n\n    ParcelFileDescriptor getModulesLog() = 17;\n\n    int getXposedVersionCode() = 18;\n\n    String getXposedVersionName() = 19;\n\n    int getXposedApiVersion() = 20;\n\n    boolean clearLogs(boolean verbose) = 21;\n\n    PackageInfo getPackageInfo(String packageName, int flags, int uid) = 22;\n\n    void forceStopPackage(String packageName, int userId) = 23;\n\n    void reboot() = 24;\n\n    boolean uninstallPackage(String packageName, int userId) = 25;\n\n    boolean isSepolicyLoaded() = 26;\n\n    List<UserInfo> getUsers() = 27;\n\n    int installExistingPackageAsUser(String packageName, int userId) = 28;\n\n    boolean systemServerRequested() = 29;\n\n    int startActivityAsUserWithFeature(in Intent intent,  int userId) = 30;\n\n    ParcelableListSlice<ResolveInfo> queryIntentActivitiesAsUser(in Intent intent, int flags, int userId) = 31;\n\n    boolean dex2oatFlagsLoaded() = 32;\n\n    void setHiddenIcon(boolean hide) = 33;\n\n    void getLogs(in ParcelFileDescriptor zipFd) = 34;\n\n    void restartFor(in Intent intent) = 35;\n\n    oneway void flashZip(String zipPath, in ParcelFileDescriptor outputStream) = 39;\n\n    boolean performDexOptMode(String packageName) = 40;\n\n    List<String> getDenyListPackages() = 41;\n\n    boolean getDexObfuscate() = 42;\n\n    void setDexObfuscate(boolean enable) = 43;\n\n    int getDex2OatWrapperCompatibility() = 44;\n\n    void clearApplicationProfileData(in String packageName) = 45;\n\n    boolean enableStatusNotification() = 47;\n\n    void setEnableStatusNotification(boolean enable) = 48;\n\n    void setLogWatchdog(boolean enable) = 49;\n\n    boolean isLogWatchdogEnabled() = 50;\n\n    boolean getAutoInclude(String packageName) = 51;\n\n    boolean setAutoInclude(String packageName, boolean enable) = 52;\n}\n"
  },
  {
    "path": "services/manager-service/src/main/aidl/org/lsposed/lspd/models/Application.aidl",
    "content": "package org.lsposed.lspd.models;\n\nparcelable Application {\n    String packageName;\n    int userId;\n}\n"
  },
  {
    "path": "services/manager-service/src/main/aidl/org/lsposed/lspd/models/UserInfo.aidl",
    "content": "package org.lsposed.lspd.models;\n\nparcelable UserInfo {\n    int id;\n    String name;\n}\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "enableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n\npluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.name = \"Vector\"\n\ninclude(\n    \":app\",\n    \":core\",\n    \":daemon\",\n    \":dex2oat\",\n    \":external:axml\",\n    \":external:apache\",\n    \":hiddenapi:stubs\",\n    \":hiddenapi:bridge\",\n    \":magisk-loader\",\n    \":services:manager-service\",\n    \":services:daemon-service\",\n    \":xposed\",\n    \":zygisk\",\n)\n"
  },
  {
    "path": "xposed/README.md",
    "content": "# Xposed API implementation of the Vector framework\n\nLSPosed is being refactored into a new project `Vector`.\n\nThis sub-project `xposed`, written in Kotlin, will be refactored from the `core` sub-project written in `Java`.\n"
  },
  {
    "path": "xposed/build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.agp.lib)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.ktfmt)\n}\n\nktfmt { kotlinLangStyle() }\n\nandroid {\n    namespace = \"org.matrix.vector.xposed\"\n\n    androidResources { enable = false }\n\n    sourceSets { named(\"main\") { java.srcDirs(\"src/main/kotlin\", \"libxposed/api/src/main/java\") } }\n}\n\ndependencies {\n    compileOnly(libs.androidx.annotation)\n    compileOnly(projects.hiddenapi.stubs)\n}\n"
  },
  {
    "path": "xposed/src/main/kotlin/org/matrix/vector/impl/utils/VectorDexParser.kt",
    "content": "package org.matrix.vector.impl.utils\n\nimport io.github.libxposed.api.utils.DexParser\nimport io.github.libxposed.api.utils.DexParser.*\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport org.matrix.vector.nativebridge.DexParserBridge\n\n/**\n * Kotlin implementation of [DexParser] for Vector.\n *\n * This class acts as a high-level wrapper around the native C++ DexParser. It maps raw JNI data\n * structures (integer arrays, flat buffers) into usable object graphs (StringId, TypeId, MethodId,\n * etc.).\n */\n@Suppress(\"UNCHECKED_CAST\")\nclass VectorDexParser(buffer: ByteBuffer, includeAnnotations: Boolean) : DexParser {\n\n    private var cookie: Long = 0\n    private val data: ByteBuffer\n\n    // Internal storage for parsed DEX structures.\n    // We use private properties and explicit getter methods as requested.\n    private val internalStrings: Array<StringId>\n    private val internalTypeIds: Array<TypeId>\n    private val internalProtoIds: Array<ProtoId>\n    private val internalFieldIds: Array<FieldId>\n    private val internalMethodIds: Array<MethodId>\n    private val internalAnnotations: Array<DexParser.Annotation>\n    private val internalArrays: Array<DexParser.Array>\n\n    init {\n        // Ensure the buffer is Direct and accessible by native code\n        data =\n            if (!buffer.isDirect || !buffer.asReadOnlyBuffer().hasArray()) {\n                ByteBuffer.allocateDirect(buffer.capacity()).apply {\n                    put(buffer)\n                    // Ensure position is reset for reading if needed,\n                    // though native uses address\n                    flip()\n                }\n            } else {\n                buffer\n            }\n\n        try {\n            val args = LongArray(2)\n            args[1] = if (includeAnnotations) 1 else 0\n\n            // Call Native Bridge\n            // Returns a raw Object[] containing headers and pools\n            val out = DexParserBridge.openDex(data, args) as Array<Any?>\n            cookie = args[0]\n\n            // --- Parse Strings (Index 0) ---\n            val rawStrings = out[0] as Array<String>\n            internalStrings = Array(rawStrings.size) { i -> VectorStringId(i, rawStrings[i]) }\n\n            // --- Parse Type IDs (Index 1) ---\n            val rawTypeIds = out[1] as IntArray\n            internalTypeIds = Array(rawTypeIds.size) { i -> VectorTypeId(i, rawTypeIds[i]) }\n\n            // --- Parse Proto IDs (Index 2) ---\n            val rawProtoIds = out[2] as Array<IntArray>\n            internalProtoIds = Array(rawProtoIds.size) { i -> VectorProtoId(i, rawProtoIds[i]) }\n\n            // --- Parse Field IDs (Index 3) ---\n            val rawFieldIds = out[3] as IntArray\n            // Each field is represented by 3 integers (class_idx, type_idx, name_idx)\n            internalFieldIds =\n                Array(rawFieldIds.size / 3) { i ->\n                    VectorFieldId(\n                        i,\n                        rawFieldIds[3 * i],\n                        rawFieldIds[3 * i + 1],\n                        rawFieldIds[3 * i + 2],\n                    )\n                }\n\n            // --- Parse Method IDs (Index 4) ---\n            val rawMethodIds = out[4] as IntArray\n            // Each method is represented by 3 integers (class_idx, proto_idx, name_idx)\n            internalMethodIds =\n                Array(rawMethodIds.size / 3) { i ->\n                    VectorMethodId(\n                        i,\n                        rawMethodIds[3 * i],\n                        rawMethodIds[3 * i + 1],\n                        rawMethodIds[3 * i + 2],\n                    )\n                }\n\n            // --- Parse Annotations (Index 5 & 6) ---\n            val rawAnnotationMetadata = out[5] as? IntArray\n            val rawAnnotationValues = out[6] as? Array<Any?>\n\n            internalAnnotations =\n                if (rawAnnotationMetadata != null && rawAnnotationValues != null) {\n                    Array(rawAnnotationMetadata.size / 2) { i ->\n                        // Metadata: [visibility, type_idx]\n                        // Values: [name_indices[], values[]]\n                        val elementsMeta = rawAnnotationValues[2 * i] as IntArray\n                        val elementsData = rawAnnotationValues[2 * i + 1] as Array<Any?>\n                        VectorAnnotation(\n                            rawAnnotationMetadata[2 * i],\n                            rawAnnotationMetadata[2 * i + 1],\n                            elementsMeta,\n                            elementsData,\n                        )\n                    }\n                } else {\n                    emptyArray()\n                }\n\n            // --- Parse Arrays (Index 7) ---\n            val rawArrays = out[7] as? Array<Any?>\n            internalArrays =\n                if (rawArrays != null) {\n                    Array(rawArrays.size / 2) { i ->\n                        val types = rawArrays[2 * i] as IntArray\n                        val values = rawArrays[2 * i + 1] as Array<Any?>\n                        VectorArray(types, values)\n                    }\n                } else {\n                    emptyArray()\n                }\n        } catch (e: Throwable) {\n            throw IOException(\"Invalid dex file\", e)\n        }\n    }\n\n    @Synchronized\n    override fun close() {\n        if (cookie != 0L) {\n            DexParserBridge.closeDex(cookie)\n            cookie = 0\n        }\n    }\n\n    override fun getStringId(): Array<StringId> = internalStrings\n\n    override fun getTypeId(): Array<TypeId> = internalTypeIds\n\n    override fun getFieldId(): Array<FieldId> = internalFieldIds\n\n    override fun getMethodId(): Array<MethodId> = internalMethodIds\n\n    override fun getProtoId(): Array<ProtoId> = internalProtoIds\n\n    override fun getAnnotations(): Array<DexParser.Annotation> = internalAnnotations\n\n    override fun getArrays(): Array<DexParser.Array> = internalArrays\n\n    override fun visitDefinedClasses(visitor: ClassVisitor) {\n        if (cookie == 0L) {\n            throw IllegalStateException(\"Closed\")\n        }\n\n        // Accessing [0] is fragile\n        val classVisitMethod = ClassVisitor::class.java.declaredMethods[0]\n        val fieldVisitMethod = FieldVisitor::class.java.declaredMethods[0]\n        val methodVisitMethod = MethodVisitor::class.java.declaredMethods[0]\n        val methodBodyVisitMethod = MethodBodyVisitor::class.java.declaredMethods[0]\n        val stopMethod = EarlyStopVisitor::class.java.declaredMethods[0]\n\n        DexParserBridge.visitClass(\n            cookie,\n            visitor,\n            FieldVisitor::class.java,\n            MethodVisitor::class.java,\n            classVisitMethod,\n            fieldVisitMethod,\n            methodVisitMethod,\n            methodBodyVisitMethod,\n            stopMethod,\n        )\n    }\n\n    /** Base implementation for all Dex IDs. */\n    private open class VectorId<Self : Id<Self>>(private val id: Int) : Id<Self> {\n        override fun getId(): Int = id\n\n        override fun compareTo(other: Self): Int = id - other.id\n    }\n\n    private inner class VectorStringId(id: Int, private val string: String) :\n        VectorId<StringId>(id), StringId {\n        override fun getString(): String = string\n    }\n\n    private inner class VectorTypeId(id: Int, descriptorIdx: Int) : VectorId<TypeId>(id), TypeId {\n        private val descriptor: StringId = internalStrings[descriptorIdx]\n\n        override fun getDescriptor(): StringId = descriptor\n    }\n\n    private inner class VectorProtoId(id: Int, protoData: IntArray) :\n        VectorId<ProtoId>(id), ProtoId {\n\n        private val shorty: StringId = internalStrings[protoData[0]]\n        private val returnType: TypeId = internalTypeIds[protoData[1]]\n        private val parameters: Array<TypeId>?\n\n        init {\n            if (protoData.size > 2) {\n                // protoData format: [shorty_idx, return_type_idx, param1_idx, param2_idx...]\n                parameters = Array(protoData.size - 2) { i -> internalTypeIds[protoData[i + 2]] }\n            } else {\n                parameters = null\n            }\n        }\n\n        override fun getShorty(): StringId = shorty\n\n        override fun getReturnType(): TypeId = returnType\n\n        override fun getParameters(): Array<TypeId>? = parameters\n    }\n\n    private inner class VectorFieldId(id: Int, classIdx: Int, typeIdx: Int, nameIdx: Int) :\n        VectorId<FieldId>(id), FieldId {\n\n        private val declaringClass: TypeId = internalTypeIds[classIdx]\n        private val type: TypeId = internalTypeIds[typeIdx]\n        private val name: StringId = internalStrings[nameIdx]\n\n        override fun getType(): TypeId = type\n\n        override fun getDeclaringClass(): TypeId = declaringClass\n\n        override fun getName(): StringId = name\n    }\n\n    private inner class VectorMethodId(id: Int, classIdx: Int, protoIdx: Int, nameIdx: Int) :\n        VectorId<MethodId>(id), MethodId {\n\n        private val declaringClass: TypeId = internalTypeIds[classIdx]\n        private val prototype: ProtoId = internalProtoIds[protoIdx]\n        private val name: StringId = internalStrings[nameIdx]\n\n        override fun getDeclaringClass(): TypeId = declaringClass\n\n        override fun getPrototype(): ProtoId = prototype\n\n        override fun getName(): StringId = name\n    }\n\n    private class VectorArray(elementsTypes: IntArray, valuesData: Array<Any?>) : DexParser.Array {\n\n        private val values: Array<Value>\n\n        init {\n            values =\n                Array(valuesData.size) { i ->\n                    VectorValue(elementsTypes[i], valuesData[i] as? ByteBuffer)\n                }\n        }\n\n        override fun getValues(): Array<Value> = values\n    }\n\n    private inner class VectorAnnotation(\n        private val visibility: Int,\n        typeIdx: Int,\n        elementNameIndices: IntArray,\n        elementValues: Array<Any?>,\n    ) : DexParser.Annotation {\n\n        private val type: TypeId = internalTypeIds[typeIdx]\n        private val elements: Array<Element>\n\n        init {\n            elements =\n                Array(elementValues.size) { i ->\n                    // Flattened structure from JNI: names are at 2*i, types at 2*i+1\n                    VectorElement(\n                        elementNameIndices[i * 2],\n                        elementNameIndices[i * 2 + 1], // valueType\n                        elementValues[i] as? ByteBuffer,\n                    )\n                }\n        }\n\n        override fun getVisibility(): Int = visibility\n\n        override fun getType(): TypeId = type\n\n        override fun getElements(): Array<Element> = elements\n    }\n\n    private open class VectorValue(private val valueType: Int, buffer: ByteBuffer?) : Value {\n\n        private val value: ByteArray?\n\n        init {\n            if (buffer != null) {\n                value = ByteArray(buffer.remaining())\n                buffer.get(value)\n            } else {\n                value = null\n            }\n        }\n\n        override fun getValue(): ByteArray? = value\n\n        override fun getValueType(): Int = valueType\n    }\n\n    private inner class VectorElement(nameIdx: Int, valueType: Int, value: ByteBuffer?) :\n        VectorValue(valueType, value), Element {\n\n        private val name: StringId = internalStrings[nameIdx]\n\n        override fun getName(): StringId = name\n    }\n}\n"
  },
  {
    "path": "xposed/src/main/kotlin/org/matrix/vector/nativebridge/DexParserBridge.kt",
    "content": "package org.matrix.vector.nativebridge\n\nimport dalvik.annotation.optimization.FastNative\nimport io.github.libxposed.api.utils.DexParser\nimport java.io.IOException\nimport java.lang.reflect.Method\nimport java.nio.ByteBuffer\n\nobject DexParserBridge {\n    @JvmStatic\n    @FastNative\n    @Throws(IOException::class)\n    external fun openDex(data: ByteBuffer, args: LongArray): Any\n\n    @JvmStatic @FastNative external fun closeDex(cookie: Long)\n\n    @JvmStatic\n    @FastNative\n    external fun visitClass(\n        cookie: Long,\n        visitor: Any,\n        fieldVisitorClass: Class<DexParser.FieldVisitor>,\n        methodVisitorClass: Class<DexParser.MethodVisitor>,\n        classVisitMethod: Method,\n        fieldVisitMethod: Method,\n        methodVisitMethod: Method,\n        methodBodyVisitMethod: Method,\n        stopMethod: Method,\n    )\n}\n"
  },
  {
    "path": "xposed/src/main/kotlin/org/matrix/vector/nativebridge/HookBridge.kt",
    "content": "package org.matrix.vector.nativebridge\n\nimport dalvik.annotation.optimization.FastNative\nimport java.lang.reflect.Executable\nimport java.lang.reflect.InvocationTargetException\nimport java.lang.reflect.Method\n\nobject HookBridge {\n    @JvmStatic\n    external fun hookMethod(\n        useModernApi: Boolean,\n        hookMethod: Executable,\n        hooker: Class<*>,\n        priority: Int,\n        callback: Any?,\n    ): Boolean\n\n    @JvmStatic\n    external fun unhookMethod(\n        useModernApi: Boolean,\n        hookMethod: Executable,\n        callback: Any?,\n    ): Boolean\n\n    @JvmStatic external fun deoptimizeMethod(method: Executable): Boolean\n\n    @JvmStatic\n    @Throws(InstantiationException::class)\n    external fun <T> allocateObject(clazz: Class<T>): T\n\n    @JvmStatic\n    @Throws(\n        IllegalAccessException::class,\n        IllegalArgumentException::class,\n        InvocationTargetException::class,\n    )\n    external fun invokeOriginalMethod(method: Executable, thisObject: Any?, vararg args: Any?): Any?\n\n    @JvmStatic\n    @Throws(\n        IllegalAccessException::class,\n        IllegalArgumentException::class,\n        InvocationTargetException::class,\n    )\n    external fun <T> invokeSpecialMethod(\n        method: Executable,\n        shorty: CharArray,\n        clazz: Class<T>,\n        thisObject: Any?,\n        vararg args: Any?,\n    ): Any?\n\n    @JvmStatic @FastNative external fun instanceOf(obj: Any?, clazz: Class<*>): Boolean\n\n    @JvmStatic @FastNative external fun setTrusted(cookie: Any?): Boolean\n\n    @JvmStatic\n    external fun callbackSnapshot(hooker_callback: Class<*>, method: Executable): Array<Array<Any?>>\n\n    @JvmStatic external fun getStaticInitializer(clazz: Class<*>): Method\n}\n"
  },
  {
    "path": "xposed/src/main/kotlin/org/matrix/vector/nativebridge/NativeAPI.kt",
    "content": "package org.matrix.vector.nativebridge\n\nobject NativeAPI {\n    @JvmStatic external fun recordNativeEntrypoint(library_name: String)\n}\n"
  },
  {
    "path": "xposed/src/main/kotlin/org/matrix/vector/nativebridge/ResourcesHook.kt",
    "content": "package org.matrix.vector.nativebridge\n\nimport android.content.res.Resources\nimport dalvik.annotation.optimization.FastNative\nimport xposed.dummy.XResourcesSuperClass\n\nobject ResourcesHook {\n    @JvmStatic external fun initXResourcesNative(): Boolean\n\n    @JvmStatic external fun makeInheritable(clazz: Class<*>): Boolean\n\n    @JvmStatic\n    external fun buildDummyClassLoader(\n        parent: ClassLoader,\n        resourceSuperClass: String,\n        typedArraySuperClass: String,\n    ): ClassLoader\n\n    @JvmStatic\n    @FastNative\n    external fun rewriteXmlReferencesNative(\n        parserPtr: Long,\n        origRes: XResourcesSuperClass,\n        repRes: Resources,\n    )\n}\n"
  },
  {
    "path": "zygisk/.gitignore",
    "content": "/build\n/release\n/.cxx\n"
  },
  {
    "path": "zygisk/README.md",
    "content": "# Vector Zygisk Module & Framework Loader\n\n## Overview\n\nThis sub-project constitutes the injection engine of the Vector framework. It acts as the bridge between the Android Zygote process and the high-level Xposed API.\n\nThe project is a hybrid system consisting of two distinct layers:\n1.  **Native Layer (C++)**: A Zygisk module that hooks process creation, filters targets, and bootstraps the environment.\n2.  **Loader Layer (Kotlin)**: The initial Java-world payload that initializes the Xposed bridge, establishes high-level IPC, and manages the \"Parasitic\" execution environment for the Manager.\n\nIts primary responsibility is to inject the Vector framework into the target process's memory at the earliest possible stage of its lifecycle, ensuring a robust and stealthy environment.\n\n---\n\n## Part 1: The Native Zygisk Layer\n\nThe native layer (`libzygisk.so`) is the entry point. It hooks into the Zygote process creation lifecycle via the Zygisk API (e.g., `preAppSpecialize`, `postAppSpecialize`). It is architected to have minimal internal logic, delegating heavy lifting (like ART hooking and ELF parsing) to the core [native](../native) library.\n\n### Core Responsibilities\n*   **Target Filtering**: Implements logic to skip isolated processes, application zygotes, and non-target system components to minimize footprint.\n*   **IPC Communication**: Establishes a secure Binder IPC connection with the daemon manager service via a \"Rendezvous\" system service to fetch the framework DEX and configuration data (e.g., obfuscation maps).\n*   **DEX Loading**: Uses `InMemoryDexClassLoader` to load the framework's bytecode directly from memory, avoiding disk I/O signatures.\n*   **JNI Interception**: Installs a low-level JNI hook on `CallBooleanMethodV`. This intercepts `Binder.execTransact` calls, allowing the framework to patch into the system's IPC flow without registering standard Android Services.\n\n### Key Components (C++)\n*   **`VectorModule` (`module.cpp`)**: The central orchestrator implementing `zygisk::ModuleBase`. It manages the injection state machine and inherits from `vector::native::Context` to gain core injection capabilities.\n*   **`IPCBridge` (`ipc_bridge.cpp`)**: A singleton handling raw Binder transactions. It manages the two-step connection protocol (Rendezvous -> Dedicated Binder) and contains the JNI table override logic.\n\n---\n\n## Part 2: The Kotlin Framework Loader\n\nOnce the native layer successfully loads the DEX, control is handed off to the Kotlin layer via JNI. This layer handles high-level Android framework manipulation, Xposed initialization, and identity spoofing.\n\n### Core Responsibilities\n*   **Bootstrapping**: `Main.forkCommon` acts as the Java entry point. It differentiates between the `system_server` and standard applications.\n*   **Parasitic Injection**: Implements the logic to run the full LSPosed Manager application inside a host process (currently `com.android.shell`). This allows the Manager to run with elevated privileges without being installed as a system app.\n*   **Manual Bridge Service**: Provides the Java-side handling for the intercepted Binder transactions.\n\n### Key Components (Kotlin)\n*   **`Main`**: The singleton entry point. It initializes the Xposed bridge (`Startup`) and decides whether to load the standard Xposed environment or the Parasitic Manager.\n*   **`BridgeService`**: The peer to the C++ `IPCBridge`. It decodes custom `_LSP` transactions, manages the distribution of the system service binder, and handles communication between the injected framework and the root daemon.\n*   **`ParasiticManagerHooker`**: The complex logic for identity transplantation.\n    *   **App Swap**: Swaps the host's `ApplicationInfo` with the Manager's info during `handleBindApplication`.\n    *   **State Persistence**: Since the Android System is unaware the host process is running Manager activities, this component manually captures and restores `Bundle` states to prevent data loss during lifecycle events.\n    *   **Resource Spoofing**: Hooks `WebView` and `ContentProvider` installation to satisfy package name validations.\n\n---\n\n## Injection & Execution Flow\n\nThe full lifecycle of a Vector-instrumented process follows this sequence:\n\n1.  **Zygote Fork**: Zygisk triggers the `preAppSpecialize` callback in C++.\n2.  **Native Decision**: `VectorModule` checks the UID/Process Name. If valid, it initializes the `IPCBridge`.\n3.  **DEX Fetch**: The C++ layer connects to the root daemon, fetches the Framework DEX file descriptor and the Obfuscation Map.\n4.  **Memory Loading**: `postAppSpecialize` triggers the creation of an `InMemoryDexClassLoader`.\n5.  **JNI Hand-off**: The native module calls the static Kotlin method `org.lsposed.lspd.core.Main.forkCommon`.\n6.  **Identity Check (Kotlin)**:\n    *   **If Manager Package**: `ParasiticManagerHooker.start()` is called. The process is \"hijacked\" to run the Manager UI.\n    *   **If Standard App**: `Startup.bootstrapXposed()` is called. Third-party modules are loaded.\n7.  **Live Interception**: Throughout the process life, the C++ JNI hook redirects specific `Binder.execTransact` calls to `BridgeService.execTransact` in Kotlin.\n\n---\n\n## Maintenance & Technical Notes\n\n### The IPC Protocol\nThe communication between the native loader and the Kotlin framework relies on specific conventions:\n*   **Transaction Code**: The custom code `_VEC` (bitwise constructed) must remain synchronized between `ipc_bridge.cpp` (Native) and `BridgeService.kt` (Kotlin).\n*   **The \"Out-Parameter\" List**: In `ParasiticManagerHooker.start()`, you will see an empty list `mutableListOf<IBinder>()`.\nIt is used as an \"out-parameter\" for the Binder call, allowing the root daemon to push the Manager Service Binder back to the loader.\n\n### System Server Hooks\nThe `ParasiticManagerSystemHooker` runs *only* in the `system_server`. It uses `XposedHooker` to intercept `ActivityTaskSupervisor.resolveActivity`. It detects Intents tagged with `LAUNCH_MANAGER` and forcefully redirects them to the parasitic process (e.g., `Shell`), modifying the `ActivityInfo` on the fly to ensure the Manager launches correctly.\n"
  },
  {
    "path": "zygisk/build.gradle.kts",
    "content": "import java.security.MessageDigest\nimport org.apache.commons.codec.binary.Hex\nimport org.apache.tools.ant.filters.ReplaceTokens\n\nplugins {\n    alias(libs.plugins.agp.app)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.ktfmt)\n}\n\nktfmt { kotlinLangStyle() }\n\nval versionCodeProvider: Provider<String> by rootProject.extra\nval versionNameProvider: Provider<String> by rootProject.extra\nval injectedPackageName: String by rootProject.extra\nval injectedPackageUid: Int by rootProject.extra\nval defaultManagerPackageName: String by rootProject.extra\n\nandroid {\n    namespace = \"org.matrix.vector\"\n\n    defaultConfig {\n        multiDexEnabled = false\n        buildConfigField(\"String\", \"InjectedPackageName\", \"\"\"\"${injectedPackageName}\"\"\"\")\n        buildConfigField(\"String\", \"ManagerPackageName\", \"\"\"\"${defaultManagerPackageName}\"\"\"\")\n\n        val flags =\n            listOf(\n                \"-DINJECTED_PACKAGE_NAME='\\\"${injectedPackageName}\\\"'\",\n                \"-DINJECTED_PACKAGE_UID=${injectedPackageUid}\",\n                \"-DMANAGER_PACKAGE_NAME='\\\"${defaultManagerPackageName}\\\"'\",\n            )\n\n        externalNativeBuild {\n            cmake {\n                cFlags.addAll(flags)\n                cppFlags.addAll(flags)\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            isMinifyEnabled = true\n            proguardFiles(\"proguard-rules.pro\")\n        }\n    }\n\n    externalNativeBuild { cmake { path(\"src/main/cpp/CMakeLists.txt\") } }\n}\n\nabstract class Injected @Inject constructor(val moduleDir: String) {\n    @get:Inject abstract val factory: ObjectFactory\n}\n\ndependencies {\n    implementation(projects.core)\n    implementation(projects.hiddenapi.bridge)\n    implementation(projects.services.managerService)\n    implementation(projects.services.daemonService)\n    compileOnly(libs.androidx.annotation)\n    compileOnly(projects.hiddenapi.stubs)\n}\n\nval zipAll = tasks.register(\"zipAll\") { group = \"Vector\" }\n\nandroidComponents {\n    onVariants(selector().all()) { variant ->\n        val variantCapped = variant.name.replaceFirstChar { it.uppercase() }\n        val variantLowered = variant.name.lowercase()\n\n        // --- Define output locations and file names ---\n        // Stage all files in a temporary directory inside 'build' before zipping\n        val tempModuleDir = project.layout.buildDirectory.dir(\"module/${variant.name}\")\n        val zipFileName =\n            \"Vector-v${versionNameProvider.get()}-${versionCodeProvider.get()}-$variantCapped.zip\"\n\n        // Using Sync ensures that stale files from previous runs are removed.\n        val prepareModuleFilesTask =\n            tasks.register<Sync>(\"prepareModuleFiles$variantCapped\") {\n                group = \"Vector Module Packaging\"\n                dependsOn(\n                    \"assemble$variantCapped\",\n                    \":app:package$variantCapped\",\n                    \":daemon:package$variantCapped\",\n                    \":dex2oat:externalNativeBuild$variantCapped\",\n                )\n                into(tempModuleDir)\n                from(\"${rootProject.projectDir}/README.md\")\n                from(\"$projectDir/module\") { exclude(\"module.prop\", \"action.sh\", \"daemon\") }\n                from(\"$projectDir/module\") {\n                    include(\"module.prop\")\n                    expand(\n                        \"versionName\" to \"v${versionNameProvider.get()}\",\n                        \"versionCode\" to versionCodeProvider.get(),\n                    )\n                }\n                from(\"$projectDir/module\") {\n                    include(\"action.sh\")\n                    val tokens =\n                        mapOf(\n                            \"MANAGER_PACKAGE_NAME\" to defaultManagerPackageName,\n                            \"INJECTED_PACKAGE_NAME\" to injectedPackageName,\n                        )\n                    filter<ReplaceTokens>(\"tokens\" to tokens)\n                }\n                from(\"$projectDir/module\") {\n                    include(\"daemon\")\n                    val tokens =\n                        mapOf(\"DEBUG\" to if (variantLowered == \"debug\") \"true\" else \"false\")\n                    filter<ReplaceTokens>(\"tokens\" to tokens)\n                }\n                from(project(\":app\").tasks.getByName(\"package$variantCapped\").outputs) {\n                    include(\"*.apk\")\n                    rename(\".*\\\\.apk\", \"manager.apk\")\n                }\n                from(project(\":daemon\").tasks.getByName(\"package$variantCapped\").outputs) {\n                    include(\"*.apk\")\n                    rename(\".*\\\\.apk\", \"daemon.apk\")\n                }\n                into(\"lib\") {\n                    val libDir = variantLowered + \"/strip${variantCapped}DebugSymbols\"\n                    from(\n                        layout.buildDirectory.dir(\n                            \"intermediates/stripped_native_libs/$libDir/out/lib\"\n                        )\n                    ) {\n                        include(\"**/libzygisk.so\")\n                    }\n                }\n                into(\"bin\") {\n                    from(\n                        project(\":dex2oat\")\n                            .layout\n                            .buildDirectory\n                            .dir(\"intermediates/cmake/$variantLowered/obj\")\n                    ) {\n                        include(\"**/dex2oat\")\n                        include(\"**/liboat_hook.so\")\n                    }\n                }\n                val dexOutPath =\n                    if (variantLowered == \"release\")\n                        layout.buildDirectory.dir(\n                            \"intermediates/dex/$variantLowered/minify${variantCapped}WithR8\"\n                        )\n                    else\n                        layout.buildDirectory.dir(\n                            \"intermediates/dex/$variantLowered/mergeDex$variantCapped\"\n                        )\n                into(\"framework\") {\n                    from(dexOutPath)\n                    rename(\"classes.dex\", \"lspd.dex\")\n                }\n                val injected = objects.newInstance<Injected>(tempModuleDir.get().asFile.path)\n                doLast {\n                    injected.factory.fileTree().from(injected.moduleDir).visit {\n                        if (isDirectory) return@visit\n                        val md = MessageDigest.getInstance(\"SHA-256\")\n                        file.forEachBlock(4096) { bytes, size -> md.update(bytes, 0, size) }\n                        File(file.path + \".sha256\").writeText(Hex.encodeHexString(md.digest()))\n                    }\n                }\n            }\n\n        val zipTask =\n            tasks.register<Zip>(\"zip${variantCapped}\") {\n                group = \"Vector Module Packaging\"\n                dependsOn(prepareModuleFilesTask)\n                archiveFileName = zipFileName\n                destinationDirectory = file(\"$projectDir/release\")\n                from(tempModuleDir)\n            }\n\n        zipAll.configure { dependsOn(zipTask) }\n\n        // A helper function to create installation tasks for different root providers.\n        fun createInstallTasks(rootProvider: String, installCli: String) {\n            val pushTask =\n                tasks.register<Exec>(\"push${rootProvider}Module${variantCapped}\") {\n                    group = \"Zygisk Module Installation\"\n                    description =\n                        \"Pushes the ${variant.name} build to the device for $rootProvider.\"\n                    dependsOn(zipTask)\n                    commandLine(\n                        \"adb\",\n                        \"push\",\n                        zipTask.get().archiveFile.get().asFile,\n                        \"/data/local/tmp\",\n                    )\n                }\n\n            val installTask =\n                tasks.register<Exec>(\"install${rootProvider}${variantCapped}\") {\n                    group = \"Zygisk Module Installation\"\n                    description = \"Installs the ${variant.name} build via $rootProvider.\"\n                    dependsOn(pushTask)\n                    commandLine(\n                        \"adb\",\n                        \"shell\",\n                        \"su\",\n                        \"-c\",\n                        \"$installCli /data/local/tmp/$zipFileName\",\n                    )\n                }\n            tasks.register<Exec>(\"install${rootProvider}AndReboot${variantCapped}\") {\n                group = \"Zygisk Module Installation\"\n                description = \"Installs the ${variant.name} build via $rootProvider and reboots.\"\n                dependsOn(installTask)\n                commandLine(\"adb\", \"reboot\")\n            }\n        }\n\n        createInstallTasks(\"Magisk\", \"magisk --install-module\")\n        createInstallTasks(\"Ksu\", \"ksud module install\")\n        createInstallTasks(\"Apatch\", \"/data/adb/apd module install\")\n    }\n}\n\nevaluationDependsOn(\":app\")\n\nevaluationDependsOn(\":daemon\")\n"
  },
  {
    "path": "zygisk/module/META-INF/com/google/android/update-binary",
    "content": "#################\n# Initialization\n#################\n\numask 022\n\n# echo before loading util_functions\nui_print() { echo \"$1\"; }\n\nrequire_new_magisk() {\n  ui_print \"*******************************\"\n  ui_print \" Please install Magisk v20.4+! \"\n  ui_print \"*******************************\"\n  exit 1\n}\n\n#########################\n# Load util_functions.sh\n#########################\n\nOUTFD=$2\nZIPFILE=$3\n\n[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk\n. /data/adb/magisk/util_functions.sh\n[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk\n\ninstall_module\nexit 0\n"
  },
  {
    "path": "zygisk/module/META-INF/com/google/android/updater-script",
    "content": "#MAGISK\n"
  },
  {
    "path": "zygisk/module/action.sh",
    "content": "MANAGER_PACKAGE_NAME=\"@MANAGER_PACKAGE_NAME@\"\nINJECTED_PACKAGE_NAME=\"@INJECTED_PACKAGE_NAME@\"\n\nam start -c \"${MANAGER_PACKAGE_NAME}.LAUNCH_MANAGER\" \"${INJECTED_PACKAGE_NAME}/.BugreportWarningActivity\"\n"
  },
  {
    "path": "zygisk/module/customize.sh",
    "content": "SKIPUNZIP=1\n\n# =========================================================\n# Utils functions to extract and verify installation package\n\nTMPDIR_FOR_VERIFY=\"$TMPDIR/.vunzip\"\nmkdir \"$TMPDIR_FOR_VERIFY\"\n\nabort_verify() {\n  ui_print \"*********************************************************\"\n  ui_print \"! $1\"\n  ui_print \"! This zip may be corrupted, please try downloading again\"\n  abort    \"*********************************************************\"\n}\n\n# Usage: extract <zip> <file> <target dir> [junk_paths: true|false]\nextract() {\n  local zip=\"$1\"\n  local file=\"$2\"\n  local dir=\"$3\"\n  local junk_paths=\"${4:-false}\" # Defaults to false if not provided\n  local opts=\"-o\"\n  local file_path hash_path file_basename\n\n  file_basename=$(basename \"$file\")\n\n  if [ \"$junk_paths\" = \"true\" ]; then\n    opts=\"-oj\"\n    file_path=\"$dir/$file_basename\"\n    hash_path=\"${TMPDIR_FOR_VERIFY}/$file_basename.sha256\"\n  else\n    file_path=\"$dir/$file\"\n    hash_path=\"${TMPDIR_FOR_VERIFY}/$file.sha256\"\n  fi\n\n  # Extract the file and its hash\n  unzip $opts \"$zip\" \"$file\" -d \"$dir\" >/dev/null 2>&1\n  [ -f \"$file_path\" ] || abort_verify \"Extracted $file does not exist\"\n\n  unzip $opts \"$zip\" \"$file.sha256\" -d \"${TMPDIR_FOR_VERIFY}\" >/dev/null 2>&1\n  [ -f \"$hash_path\" ] || abort_verify \"Hash file $file.sha256 does not exist\"\n\n  # Read the expected hash and verify it\n  local expected_hash\n  read -r expected_hash < \"$hash_path\"\n  expected_hash=\"${expected_hash%% *}\" # Strip anything after the actual hash string\n\n  if ! echo \"$expected_hash  $file_path\" | sha256sum -c -s -; then\n    abort_verify \"Failed to verify $file\"\n  fi\n\n  ui_print \"- Verified $file\"\n}\n# =========================================================\n\nVERSION=$(grep_prop version \"${TMPDIR}/module.prop\")\nui_print \"- Vector version ${VERSION}\"\n\n# Disable existing LSPosed installation\nLSPOSED_DIR=\"/data/adb/modules/zygisk_lsposed\"\nif [ -d \"$LSPOSED_DIR\" ]; then\n    ui_print \"*********************************************************\"\n    ui_print \"LSPosed installation detected, disabling it for Vector\"\n    touch \"$LSPOSED_DIR/disable\"\n    ui_print \"*********************************************************\"\nfi\n\n# 1. Map architecture to standard ABI paths, eliminating duplicate logic\ncase \"$ARCH\" in\n    arm|arm64)\n        ABI32=\"armeabi-v7a\"\n        ABI64=\"arm64-v8a\"\n        ;;\n    x86|x64)\n        ABI32=\"x86\"\n        ABI64=\"x86_64\"\n        ;;\n    *)\n        abort \"! Unsupported platform: $ARCH\"\n        ;;\nesac\nui_print \"- Device platform: $ARCH ($ABI32 / $ABI64)\"\n\nui_print \"- Extracting root module files\"\nfor file in module.prop action.sh service.sh uninstall.sh sepolicy.rule framework/lspd.dex daemon.apk daemon manager.apk; do\n    extract \"$ZIPFILE\" \"$file\" \"$MODPATH\"\ndone\n\nui_print \"- Extracting Zygisk libraries\"\nmkdir -p \"$MODPATH/zygisk\"\n\n# Extract 32-bit lib\nextract \"$ZIPFILE\" \"lib/$ABI32/libzygisk.so\" \"$MODPATH/zygisk\" true\nmv \"$MODPATH/zygisk/libzygisk.so\" \"$MODPATH/zygisk/${ABI32}.so\"\n\n# Extract 64-bit lib if supported\nif [ \"$IS64BIT\" = true ]; then\n    extract \"$ZIPFILE\" \"lib/$ABI64/libzygisk.so\" \"$MODPATH/zygisk\" true\n    mv \"$MODPATH/zygisk/libzygisk.so\" \"$MODPATH/zygisk/${ABI64}.so\"\nfi\n\nif [ \"$API\" -ge 29 ]; then\n    ui_print \"- Extracting dex2oat binaries\"\n    mkdir -p \"$MODPATH/bin\"\n\n    # Extract 32-bit binaries\n    extract \"$ZIPFILE\" \"bin/$ABI32/dex2oat\" \"$MODPATH/bin\" true\n    extract \"$ZIPFILE\" \"bin/$ABI32/liboat_hook.so\" \"$MODPATH/bin\" true\n    mv \"$MODPATH/bin/dex2oat\" \"$MODPATH/bin/dex2oat32\"\n    mv \"$MODPATH/bin/liboat_hook.so\" \"$MODPATH/bin/liboat_hook32.so\"\n\n    # Extract 64-bit binaries\n    if [ \"$IS64BIT\" = true ]; then\n        extract \"$ZIPFILE\" \"bin/$ABI64/dex2oat\" \"$MODPATH/bin\" true\n        extract \"$ZIPFILE\" \"bin/$ABI64/liboat_hook.so\" \"$MODPATH/bin\" true\n        mv \"$MODPATH/bin/dex2oat\" \"$MODPATH/bin/dex2oat64\"\n        mv \"$MODPATH/bin/liboat_hook.so\" \"$MODPATH/bin/liboat_hook64.so\"\n    fi\n\n    ui_print \"- Patching binaries for anti-detection\"\n    DEV_PATH=$(tr -dc 'a-z0-9' </dev/urandom | head -c 32)\n    # Patch only if the file successfully exists\n    [ -f \"$MODPATH/daemon.apk\" ] && sed -i \"s/5291374ceda0aef7c5d86cd2a4f6a3ac/$DEV_PATH/g\" \"$MODPATH/daemon.apk\"\n    [ -f \"$MODPATH/bin/dex2oat32\" ] && sed -i \"s/5291374ceda0aef7c5d86cd2a4f6a3ac/$DEV_PATH/\" \"$MODPATH/bin/dex2oat32\"\n    [ -f \"$MODPATH/bin/dex2oat64\" ] && sed -i \"s/5291374ceda0aef7c5d86cd2a4f6a3ac/$DEV_PATH/\" \"$MODPATH/bin/dex2oat64\"\nelse\n    extract \"$ZIPFILE\" 'system.prop' \"$MODPATH\"\nfi\n\nui_print \"- Setting permissions\"\nset_perm_recursive \"$MODPATH\" 0 0 0755 0644\n[ -d \"$MODPATH/bin\" ] && set_perm_recursive \"$MODPATH/bin\" 0 2000 0755 0755 u:object_r:xposed_file:s0\n\nset_perm \"$MODPATH/daemon\" 0 0 0744\n\nif [ \"$(grep_prop ro.maple.enable)\" = \"1\" ]; then\n    ui_print \"- Add ro.maple.enable=0\"\n    echo \"ro.maple.enable=0\" >>\"$MODPATH/system.prop\"\nfi\n\nui_print \"- Welcome to Vector!\"\n"
  },
  {
    "path": "zygisk/module/daemon",
    "content": "#!/system/bin/sh\n\ndir=\"${0%/*}\"\ntmpDaemonApk=\"/data/local/tmp/daemon.apk\"\ndebug=\"@DEBUG@\"\n\n# Safely check for debug APK and set classpath\nif [ -r \"$tmpDaemonApk\" ]; then\n  java_options=\"-Djava.class.path=$tmpDaemonApk\"\n  debug=\"true\"\nelse\n  java_options=\"-Djava.class.path=$dir/daemon.apk\"\nfi\n\n# Apply debug options based on Android SDK version.\n# Reference: https://developer.android.com/ndk/guides/wrap-script#debugging_when_using_wrapsh\nif [ \"$debug\" = \"true\" ]; then\n  os_version=$(getprop ro.build.version.sdk)\n  if [ \"$os_version\" -eq \"27\" ]; then\n    java_options=\"$java_options -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable\"\n  elif [ \"$os_version\" -eq \"28\" ]; then\n    java_options=\"$java_options -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable\"\n  else\n    java_options=\"$java_options -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y\"\n  fi\nfi\n\n# Mount an empty temporary file system over /data/resource-cache to avoid possible overlay conflicts.\n# Note that this script runs inside an isolated mount namespace (via unshare).\nmount tmpfs -t tmpfs /data/resource-cache\n\n# Wait for Zygote socket using a polling loop\nif [ ! -S \"/dev/socket/zygote\" ]; then\n  # Wait up to ~10 seconds to avoid an infinite hang if something breaks\n  wait_count=0\n  while [ ! -S \"/dev/socket/zygote\" ] &&[ \"$wait_count\" -lt 100 ]; do\n    sleep 0.1\n    wait_count=$((wait_count + 1))\n  done\n  [ \"$debug\" = \"true\" ] && log -p v -t \"Vector\" \"zygote started\"\nfi\n\n[ \"$debug\" = \"true\" ] && log -p d -t \"Vector\" \"Starting daemon $*\"\n\n# Launch the daemon\nexec /system/bin/app_process $java_options /system/bin --nice-name=lspd org.lsposed.lspd.Main \"$@\" >/dev/null 2>&1\n"
  },
  {
    "path": "zygisk/module/module.prop",
    "content": "id=zygisk_vector\nname=Vector\nversion=${versionName} (${versionCode})\nversionCode=${versionCode}\nauthor=JingMatrix\ndescription=A modern, Xposed-compatible framework for Android application hooking. (Android 8.1 ~ 16)\nupdateJson=https://raw.githubusercontent.com/JingMatrix/Vector/master/zygisk/update.json\n"
  },
  {
    "path": "zygisk/module/sepolicy.rule",
    "content": "allow dex2oat dex2oat_exec file execute_no_trans\nallow dex2oat system_linker_exec file execute_no_trans\n\nallow shell shell dir write\n\ntype xposed_file file_type\ntypeattribute xposed_file mlstrustedobject\nallow {dex2oat installd isolated_app shell} xposed_file {file dir} *\n\nallow dex2oat {unlabeled tmpfs} file *\n\ntype xposed_data file_type\ntypeattribute xposed_data mlstrustedobject\nallow * xposed_data {file dir} *\n"
  },
  {
    "path": "zygisk/module/service.sh",
    "content": "# Extract the directory path and change directory \nMODDIR=\"${0%/*}\"\ncd \"$MODDIR\" || exit 1\n\n# Start the daemon directly in the background within a private mount namespace\nunshare --propagation slave -m \"$MODDIR/daemon\" --system-server-max-retry=3 \"$@\" &\n"
  },
  {
    "path": "zygisk/module/system.prop",
    "content": "dalvik.vm.dex2oat-flags=--inline-max-code-units=0\n"
  },
  {
    "path": "zygisk/module/uninstall.sh",
    "content": "rm -rf /data/adb/lspd\n"
  },
  {
    "path": "zygisk/proguard-rules.pro",
    "content": "-keepclasseswithmembers class org.matrix.vector.core.Main {\n    public static void forkCommon(boolean, boolean, java.lang.String, java.lang.String, android.os.IBinder);\n}\n-keepclasseswithmembers,includedescriptorclasses class * {\n    native <methods>;\n}\n-keepclasseswithmembers class org.matrix.vector.service.BridgeService {\n    public static boolean *(android.os.IBinder, int, long, long, int);\n}\n\n-assumenosideeffects class android.util.Log {\n    public static *** v(...);\n    public static *** d(...);\n}\n-repackageclasses\n-allowaccessmodification\n-dontwarn org.lsposed.lspd.core.*\n-dontwarn org.lsposed.lspd.util.Hookers\n"
  },
  {
    "path": "zygisk/src/main/AndroidManifest.xml",
    "content": "<manifest />\n"
  },
  {
    "path": "zygisk/src/main/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\nproject(zygisk)\n\nset(CMAKE_CXX_STANDARD 23)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\nadd_subdirectory(${VECTOR_ROOT}/native native)\n\nadd_library(${PROJECT_NAME} SHARED module.cpp ipc_bridge.cpp)\ntarget_include_directories(${PROJECT_NAME} PUBLIC include)\n\ntarget_link_libraries(${PROJECT_NAME} native log)\n\nif (DEFINED DEBUG_SYMBOLS_PATH)\n    message(STATUS \"Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}\")\n    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD\n            COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}\n            COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:${PROJECT_NAME}>\n            ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug\n            COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>\n            COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug\n            $<TARGET_FILE:${PROJECT_NAME}>)\nendif()\n"
  },
  {
    "path": "zygisk/src/main/cpp/include/ipc_bridge.h",
    "content": "#pragma once\n\n#include <jni.h>\n\n#include <map>\n#include <string>\n#include <tuple>\n\n// This module is a client of the 'native' library.\n// We include the JNI bridge from 'native' to leverage its helper functions.\n#include <jni/jni_bridge.h>\n\nnamespace vector::native::module {\n\n/**\n * @class IPCBridge\n * @brief Manages Binder IPC communication with the Vector host service.\n *\n * This singleton class is the communication arm of the Zygisk module. Its key responsibilities are:\n * 1. Discovering and connecting to the central host service (the \"manager\").\n * 2. Requesting the framework's DEX file and obfuscation map from the service.\n * 3. Caching all necessary JNI class and method IDs for efficient reuse.\n */\nclass IPCBridge {\npublic:\n    // Enforce singleton pattern.\n    IPCBridge(const IPCBridge &) = delete;\n    IPCBridge &operator=(const IPCBridge &) = delete;\n\n    /**\n     * @brief Gets the singleton instance of the IPCBridge.\n     */\n    static IPCBridge &GetInstance();\n\n    /**\n     * @brief Caches JNI class and method IDs needed for Binder communication.\n     * @param env A valid JNI environment pointer.\n     */\n    void Initialize(JNIEnv *env);\n\n    /**\n     * @brief Requests an application-specific Binder from the host service.\n     * @param env JNI environment pointer.\n     * @param nice_name The process name.\n     * @return A ScopedLocalRef to the Binder object, or nullptr on failure.\n     */\n    lsplant::ScopedLocalRef<jobject> RequestAppBinder(JNIEnv *env, jstring nice_name);\n\n    /**\n     * @brief Requests the system_server's dedicated Binder from the host service.\n     * @param env JNI environment pointer.\n     * @param bridgeServiceName rendezvous point used by the system_server\n     * @return A ScopedLocalRef to the Binder object, or nullptr on failure.\n     */\n    lsplant::ScopedLocalRef<jobject> RequestSystemServerBinder(JNIEnv *env,\n                                                               std::string bridgeServiceName);\n\n    /**\n     * @brief Asks the system_server binder for the application manager binder.\n     * @param env JNI environment pointer.\n     * @param system_server_binder The binder connected to the system server service.\n     * @return A ScopedLocalRef to the application manager Binder, or nullptr on failure.\n     */\n    lsplant::ScopedLocalRef<jobject> RequestManagerBinderFromSystemServer(\n        JNIEnv *env, jobject system_server_binder);\n\n    /**\n     * @brief Fetches the framework DEX file via the provided Binder connection.\n     * @param env JNI environment pointer.\n     * @param binder A live Binder connection to the host service.\n     * @return A tuple containing the file descriptor and size of the DEX file.\n     *         Returns {-1, 0} on failure.\n     */\n    std::tuple<int, size_t> FetchFrameworkDex(JNIEnv *env, jobject binder);\n\n    /**\n     * @brief Fetches the framework's obfuscation map via the provided Binder.\n     * @param env JNI environment pointer.\n     * @param binder A live Binder connection to the host service.\n     * @return A map of original names to obfuscated names.\n     */\n    std::map<std::string, std::string> FetchObfuscationMap(JNIEnv *env, jobject binder);\n\n    /**\n     * @brief Sets up the JNI hook to intercept Binder transactions.\n     *\n     * This is the core of the IPC interception mechanism.\n     * It replaces the JNI function pointer for CallBooleanMethodV to\n     * inspect calls to Binder.execTransact, allowing the framework to\n     * handle its own custom transaction codes directly.\n     * @param env JNI environment pointer.\n     */\n    void HookBridge(JNIEnv *env);\n\nprivate:\n    /**\n     * @class ParcelWrapper\n     * @brief A private RAII wrapper to ensure Parcel objects are always recycled.\n     *\n     * As a private nested class, it has access to the private members of IPCBridge\n     * (like parcel_class_ and recycle_method_).\n     */\n    class ParcelWrapper {\n    public:\n        ParcelWrapper(JNIEnv *env, IPCBridge *bridge);\n        ~ParcelWrapper();\n        // Disable copy operations\n        ParcelWrapper(const ParcelWrapper &) = delete;\n        ParcelWrapper &operator=(const ParcelWrapper &) = delete;\n\n        lsplant::ScopedLocalRef<jobject> data;\n        lsplant::ScopedLocalRef<jobject> reply;\n\n    private:\n        JNIEnv *env_;\n        IPCBridge *bridge_;\n    };\n\n    // Private constructor for singleton.\n    IPCBridge() = default;\n\n    bool initialized_ = false;\n\n    // --- Cached JNI References ---\n    // These are initialized once and stored as global references for performance.\n\n    // android.os.ServiceManager\n    jclass service_manager_class_ = nullptr;\n    jmethodID get_service_method_ = nullptr;\n\n    // android.os.IBinder\n    jmethodID transact_method_ = nullptr;\n\n    // android.os.Binder\n    jclass binder_class_ = nullptr;\n    jmethodID binder_ctor_ = nullptr;\n\n    // android.os.Parcel\n    jclass parcel_class_ = nullptr;\n    jmethodID obtain_method_ = nullptr;\n    jmethodID recycle_method_ = nullptr;\n    jmethodID write_interface_token_method_ = nullptr;\n    jmethodID write_int_method_ = nullptr;\n    jmethodID write_string_method_ = nullptr;\n    jmethodID write_strong_binder_method_ = nullptr;\n    jmethodID read_exception_method_ = nullptr;\n    jmethodID read_strong_binder_method_ = nullptr;\n    jmethodID read_file_descriptor_method_ = nullptr;\n    jmethodID read_int_method_ = nullptr;\n    jmethodID read_long_method_ = nullptr;\n    jmethodID read_string_method_ = nullptr;\n\n    // android.os.ParcelFileDescriptor\n    jclass parcel_fd_class_ = nullptr;\n    jmethodID detach_fd_method_ = nullptr;\n\n    // --- JNI Hooking Members ---\n    // These are required to store the state for our JNI function table override.\n\n    // The C++ hook function that will replace the original CallBooleanMethodV.\n    static jboolean JNICALL CallBooleanMethodV_Hook(JNIEnv *env, jobject obj, jmethodID methodId,\n                                                    va_list args);\n    // The helper function that handles our specific transaction code.\n    static jboolean ExecTransact_Replace(jboolean *res, JNIEnv *env, jobject obj, va_list args);\n\n    // A complete copy of the original JNI function table, with our hook installed.\n    JNINativeInterface native_interface_hook_{};\n    // The original jmethodID for android.os.Binder.execTransact().\n    jmethodID exec_transact_backup_method_id_ = nullptr;\n    // A function pointer to the original CallBooleanMethodV implementation.\n    jboolean (*call_boolean_method_v_backup_)(JNIEnv *, jobject, jmethodID, va_list) = nullptr;\n\n    // A global reference to the framework's Java BridgeService class.\n    jclass bridge_service_class_ = nullptr;\n    // The jmethodID for the static Java method that handles the intercepted transaction.\n    jmethodID exec_transact_replace_method_id_ = nullptr;\n};\n\n}  // namespace vector::native::module\n"
  },
  {
    "path": "zygisk/src/main/cpp/include/zygisk.hpp",
    "content": "/* Copyright 2022-2023 John \"topjohnwu\" Wu\n *\n * Permission to use, copy, modify, and/or distribute this software for any\n * purpose with or without fee is hereby granted.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n */\n\n// This is the public API for Zygisk modules.\n// DO NOT MODIFY ANY CODE IN THIS HEADER.\n\n#pragma once\n\n#include <jni.h>\n#include <sys/types.h>\n\n#define ZYGISK_API_VERSION 4\n\n/*\n\n***************\n* Introduction\n***************\n\nOn Android, all app processes are forked from a special daemon called \"Zygote\".\nFor each new app process, zygote will fork a new process and perform \"specialization\".\nThis specialization operation enforces the Android security sandbox on the newly forked\nprocess to make sure that 3rd party application code is only loaded after it is being\nrestricted within a sandbox.\n\nOn Android, there is also this special process called \"system_server\". This single\nprocess hosts a significant portion of system services, which controls how the\nAndroid operating system and apps interact with each other.\n\nThe Zygisk framework provides a way to allow developers to build modules and run custom\ncode before and after system_server and any app processes' specialization.\nThis enable developers to inject code and alter the behavior of system_server and app processes.\n\nPlease note that modules will only be loaded after zygote has forked the child process.\nTHIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON!\n\n*********************\n* Development Guide\n*********************\n\nDefine a class and inherit zygisk::ModuleBase to implement the functionality of your module.\nUse the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk.\n\nExample code:\n\nstatic jint (*orig_logger_entry_max)(JNIEnv *env);\nstatic jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }\n\nclass ExampleModule : public zygisk::ModuleBase {\npublic:\n    void onLoad(zygisk::Api *api, JNIEnv *env) override {\n        this->api = api;\n        this->env = env;\n    }\n    void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {\n        JNINativeMethod methods[] = {\n            { \"logger_entry_max_payload_native\", \"()I\", (void*) my_logger_entry_max },\n        };\n        api->hookJniNativeMethods(env, \"android/util/Log\", methods, 1);\n        *(void **) &orig_logger_entry_max = methods[0].fnPtr;\n    }\nprivate:\n    zygisk::Api *api;\n    JNIEnv *env;\n};\n\nREGISTER_ZYGISK_MODULE(ExampleModule)\n\n-----------------------------------------------------------------------------------------\n\nSince your module class's code runs with either Zygote's privilege in pre[XXX]Specialize,\nor runs in the sandbox of the target process in post[XXX]Specialize, the code in your class\nnever runs in a true superuser environment.\n\nIf your module require access to superuser permissions, you can create and register\na root companion handler function. This function runs in a separate root companion\ndaemon process, and an Unix domain socket is provided to allow you to perform IPC between\nyour target process and the root companion process.\n\nExample code:\n\nstatic void example_handler(int socket) { ... }\n\nREGISTER_ZYGISK_COMPANION(example_handler)\n\n*/\n\nnamespace zygisk {\n\nstruct Api;\nstruct AppSpecializeArgs;\nstruct ServerSpecializeArgs;\n\nclass ModuleBase {\npublic:\n    // This method is called as soon as the module is loaded into the target process.\n    // A Zygisk API handle will be passed as an argument.\n    virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}\n\n    // This method is called before the app process is specialized.\n    // At this point, the process just got forked from zygote, but no app specific specialization\n    // is applied. This means that the process does not have any sandbox restrictions and\n    // still runs with the same privilege of zygote.\n    //\n    // All the arguments that will be sent and used for app specialization is passed as a single\n    // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app\n    // process will be specialized.\n    //\n    // If you need to run some operations as superuser, you can call Api::connectCompanion() to\n    // get a socket to do IPC calls with a root companion process.\n    // See Api::connectCompanion() for more info.\n    virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}\n\n    // This method is called after the app process is specialized.\n    // At this point, the process has all sandbox restrictions enabled for this application.\n    // This means that this method runs with the same privilege of the app's own code.\n    virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}\n\n    // This method is called before the system server process is specialized.\n    // See preAppSpecialize(args) for more info.\n    virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}\n\n    // This method is called after the system server process is specialized.\n    // At this point, the process runs with the privilege of system_server.\n    virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}\n};\n\nstruct AppSpecializeArgs {\n    // Required arguments. These arguments are guaranteed to exist on all Android versions.\n    jint &uid;\n    jint &gid;\n    jintArray &gids;\n    jint &runtime_flags;\n    jobjectArray &rlimits;\n    jint &mount_external;\n    jstring &se_info;\n    jstring &nice_name;\n    jstring &instruction_set;\n    jstring &app_data_dir;\n\n    // Optional arguments. Please check whether the pointer is null before de-referencing\n    jintArray *const fds_to_ignore;\n    jboolean *const is_child_zygote;\n    jboolean *const is_top_app;\n    jobjectArray *const pkg_data_info_list;\n    jobjectArray *const whitelisted_data_info_list;\n    jboolean *const mount_data_dirs;\n    jboolean *const mount_storage_dirs;\n\n    AppSpecializeArgs() = delete;\n};\n\nstruct ServerSpecializeArgs {\n    jint &uid;\n    jint &gid;\n    jintArray &gids;\n    jint &runtime_flags;\n    jlong &permitted_capabilities;\n    jlong &effective_capabilities;\n\n    ServerSpecializeArgs() = delete;\n};\n\nnamespace internal {\nstruct api_table;\ntemplate <class T>\nvoid entry_impl(api_table *, JNIEnv *);\n}  // namespace internal\n\n// These values are used in Api::setOption(Option)\nenum Option : int {\n    // Force Magisk's denylist unmount routines to run on this process.\n    //\n    // Setting this option only makes sense in preAppSpecialize.\n    // The actual unmounting happens during app process specialization.\n    //\n    // Set this option to force all Magisk and modules' files to be unmounted from the\n    // mount namespace of the process, regardless of the denylist enforcement status.\n    FORCE_DENYLIST_UNMOUNT = 0,\n\n    // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize.\n    // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory.\n    // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS.\n    DLCLOSE_MODULE_LIBRARY = 1,\n};\n\n// Bit masks of the return value of Api::getFlags()\nenum StateFlag : uint32_t {\n    // The user has granted root access to the current process\n    PROCESS_GRANTED_ROOT = (1u << 0),\n\n    // The current process was added on the denylist\n    PROCESS_ON_DENYLIST = (1u << 1),\n};\n\n// All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded\n// from the specialized process afterwards.\nstruct Api {\n    // Connect to a root companion process and get a Unix domain socket for IPC.\n    //\n    // This API only works in the pre[XXX]Specialize methods due to SELinux restrictions.\n    //\n    // The pre[XXX]Specialize methods run with the same privilege of zygote.\n    // If you would like to do some operations with superuser permissions, register a handler\n    // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func).\n    // Another good use case for a companion process is that if you want to share some resources\n    // across multiple processes, hold the resources in the companion process and pass it over.\n    //\n    // The root companion process is ABI aware; that is, when calling this method from a 32-bit\n    // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit.\n    //\n    // Returns a file descriptor to a socket that is connected to the socket passed to your\n    // module's companion request handler. Returns -1 if the connection attempt failed.\n    int connectCompanion();\n\n    // Get the file descriptor of the root folder of the current module.\n    //\n    // This API only works in the pre[XXX]Specialize methods.\n    // Accessing the directory returned is only possible in the pre[XXX]Specialize methods\n    // or in the root companion process (assuming that you sent the fd over the socket).\n    // Both restrictions are due to SELinux and UID.\n    //\n    // Returns -1 if errors occurred.\n    int getModuleDir();\n\n    // Set various options for your module.\n    // Please note that this method accepts one single option at a time.\n    // Check zygisk::Option for the full list of options available.\n    void setOption(Option opt);\n\n    // Get information about the current process.\n    // Returns bitwise-or'd zygisk::StateFlag values.\n    uint32_t getFlags();\n\n    // Exempt the provided file descriptor from being automatically closed.\n    //\n    // This API only make sense in preAppSpecialize; calling this method in any other situation\n    // is either a no-op (returns true) or an error (returns false).\n    //\n    // When false is returned, the provided file descriptor will eventually be closed by zygote.\n    bool exemptFd(int fd);\n\n    // Hook JNI native methods for a class\n    //\n    // Lookup all registered JNI native methods and replace it with your own methods.\n    // The original function pointer will be saved in each JNINativeMethod's fnPtr.\n    // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr\n    // will be set to nullptr.\n    void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods,\n                              int numMethods);\n\n    // Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory.\n    //\n    // Parsing /proc/[PID]/maps will give you the memory map of a process. As an example:\n    //\n    //       <address>       <perms>  <offset>   <dev>  <inode>           <pathname>\n    // 56b4346000-56b4347000  r-xp    00002000   fe:00    235       /system/bin/app_process64\n    // (More details: https://man7.org/linux/man-pages/man5/proc.5.html)\n    //\n    // The `dev` and `inode` pair uniquely identifies a file being mapped into memory.\n    // For matching ELFs loaded in memory, replace function `symbol` with `newFunc`.\n    // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.\n    void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc);\n\n    // Commit all the hooks that was previously registered.\n    // Returns false if an error occurred.\n    bool pltHookCommit();\n\nprivate:\n    internal::api_table *tbl;\n    template <class T>\n    friend void internal::entry_impl(internal::api_table *, JNIEnv *);\n};\n\n// Register a class as a Zygisk module\n\n#define REGISTER_ZYGISK_MODULE(clazz)                                                              \\\n    void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) {                    \\\n        zygisk::internal::entry_impl<clazz>(table, env);                                           \\\n    }\n\n// Register a root companion request handler function for your module\n//\n// The function runs in a superuser daemon process and handles a root companion request from\n// your module running in a target process. The function has to accept an integer value,\n// which is a Unix domain socket that is connected to the target process.\n// See Api::connectCompanion() for more info.\n//\n// NOTE: the function can run concurrently on multiple threads.\n// Be aware of race conditions if you have globally shared resources.\n\n#define REGISTER_ZYGISK_COMPANION(func)                                                            \\\n    void zygisk_companion_entry(int client) { func(client); }\n\n/*********************************************************\n * The following is internal ABI implementation detail.\n * You do not have to understand what it is doing.\n *********************************************************/\n\nnamespace internal {\n\nstruct module_abi {\n    long api_version;\n    ModuleBase *impl;\n\n    void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *);\n    void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *);\n    void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *);\n    void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *);\n\n    module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) {\n        preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); };\n        postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); };\n        preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); };\n        postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); };\n    }\n};\n\nstruct api_table {\n    // Base\n    void *impl;\n    bool (*registerModule)(api_table *, module_abi *);\n\n    void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);\n    void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **);\n    bool (*exemptFd)(int);\n    bool (*pltHookCommit)();\n    int (*connectCompanion)(void * /* impl */);\n    void (*setOption)(void * /* impl */, Option);\n    int (*getModuleDir)(void * /* impl */);\n    uint32_t (*getFlags)(void * /* impl */);\n};\n\ntemplate <class T>\nvoid entry_impl(api_table *table, JNIEnv *env) {\n    static Api api;\n    api.tbl = table;\n    static T module;\n    ModuleBase *m = &module;\n    static module_abi abi(m);\n    if (!table->registerModule(table, &abi)) return;\n    m->onLoad(&api, env);\n}\n\n}  // namespace internal\n\ninline int Api::connectCompanion() {\n    return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1;\n}\ninline int Api::getModuleDir() { return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1; }\ninline void Api::setOption(Option opt) {\n    if (tbl->setOption) tbl->setOption(tbl->impl, opt);\n}\ninline uint32_t Api::getFlags() { return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0; }\ninline bool Api::exemptFd(int fd) { return tbl->exemptFd != nullptr && tbl->exemptFd(fd); }\ninline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods,\n                                      int numMethods) {\n    if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);\n}\ninline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc,\n                                 void **oldFunc) {\n    if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc);\n}\ninline bool Api::pltHookCommit() { return tbl->pltHookCommit != nullptr && tbl->pltHookCommit(); }\n\n}  // namespace zygisk\n\nextern \"C\" {\n\n[[gnu::visibility(\"default\"), maybe_unused]]\nvoid zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *);\n\n[[gnu::visibility(\"default\"), maybe_unused]]\nvoid zygisk_companion_entry(int);\n\n}  // extern \"C\"\n"
  },
  {
    "path": "zygisk/src/main/cpp/ipc_bridge.cpp",
    "content": "#include \"ipc_bridge.h\"\n\n#include <elf/elf_image.h>\n#include <elf/symbol_cache.h>\n#include <unistd.h>\n\n#include <atomic>\n#include <string_view>\n#include <thread>\n\nusing namespace std::literals::string_view_literals;\n\n#include <common/logging.h>\n\nnamespace vector::native::module {\n\n// Store the ID of the last caller whose framework transaction failed.\n// It's initialized to a value that won't match.\nstatic std::atomic<uint64_t> g_last_failed_id = ~0;\n\n/**\n * @class BinderCaller\n * @brief A helper to get the UID and PID of the current Binder caller.\n *\n * This class encapsulates the logic for finding and calling private functions\n * within libbinder.so to identify the origin of an IPC transaction.\n */\nclass BinderCaller {\npublic:\n    /**\n     * @brief Initializes the helper by finding the required function pointers.\n     * This must be called once before GetId() is used.\n     */\n    static void Initialize() {\n        // Use the native library's symbol cache to find symbols in the loaded libbinder.so\n        auto libbinder = ElfSymbolCache::GetLibBinder();\n        if (!libbinder) {\n            LOGW(\"libbinder.so not found in cache, cannot get caller ID.\");\n            return;\n        }\n\n        s_self_or_null_fn = (IPCThreadState * (*)()) libbinder->getSymbAddress(\n            \"_ZN7android14IPCThreadState10selfOrNullEv\");\n        s_get_calling_pid_fn = (pid_t(*)(IPCThreadState *))libbinder->getSymbAddress(\n            \"_ZNK7android14IPCThreadState13getCallingPidEv\");\n        s_get_calling_uid_fn = (uid_t(*)(IPCThreadState *))libbinder->getSymbAddress(\n            \"_ZNK7android14IPCThreadState13getCallingUidEv\");\n\n        if (!s_self_or_null_fn || !s_get_calling_pid_fn || !s_get_calling_uid_fn) {\n            LOGW(\"Could not resolve all IPCThreadState symbols. Caller ID check will be disabled.\");\n        } else {\n            LOGV(\"IPCThreadState symbols resolved successfully.\");\n        }\n    }\n\n    /**\n     * @brief Gets the unique 64-bit ID of the current Binder caller.\n     * @return A combined UID and PID, or 0 if symbols are not available.\n     */\n    static uint64_t GetId() {\n        if (!s_self_or_null_fn) [[unlikely]]\n            return 0;\n\n        IPCThreadState *self = s_self_or_null_fn();\n        if (!self) return 0;\n\n        auto uid = s_get_calling_uid_fn(self);\n        auto pid = s_get_calling_pid_fn(self);\n        return (static_cast<uint64_t>(uid) << 32) | pid;\n    }\n\nprivate:\n    // Forward declare the opaque struct from libbinder.\n    struct IPCThreadState;\n\n    inline static IPCThreadState *(*s_self_or_null_fn)() = nullptr;\n    inline static pid_t (*s_get_calling_pid_fn)(IPCThreadState *) = nullptr;\n    inline static uid_t (*s_get_calling_uid_fn)(IPCThreadState *) = nullptr;\n};\n\n// --- Binder IPC Protocol Constants ---\n// These are the \"secret handshakes\" used to communicate with the Vector manager service.\n\n// The name of the system service we use as a rendezvous point to find our manager service.\n// Using \"activity\" is a common technique as it's always available.\nconstexpr auto kBridgeServiceName = \"activity\"sv;\n\n// Transaction codes for specific actions.\nconstexpr jint kBridgeTransactionCode = ('_' << 24) | ('V' << 16) | ('E' << 8) | 'C';\nconstexpr jint kDexTransactionCode = ('_' << 24) | ('D' << 16) | ('E' << 8) | 'X';\nconstexpr jint kObfuscationMapTransactionCode = ('_' << 24) | ('O' << 16) | ('B' << 8) | 'F';\n\n// Action codes sent within a kBridgeTransactionCode transaction.\nconstexpr jint kActionGetBinder = 2;\n\n// =========================================================================================\n// Implementation of IPCBridge::ParcelWrapper (Private Nested Class)\n// =========================================================================================\n\n/**\n * @brief Constructs the ParcelWrapper, obtaining two new Parcel objects from the pool.\n * @param env A valid JNI environment pointer.\n * @param bridge A pointer to the parent IPCBridge instance to access its cached JNI IDs.\n */\nIPCBridge::ParcelWrapper::ParcelWrapper(JNIEnv *env, IPCBridge *bridge)\n    : data(lsplant::JNI_CallStaticObjectMethod(env, bridge->parcel_class_, bridge->obtain_method_)),\n      reply(\n          lsplant::JNI_CallStaticObjectMethod(env, bridge->parcel_class_, bridge->obtain_method_)),\n      env_(env),\n      bridge_(bridge) {}\n\n/**\n * @brief Destructs the ParcelWrapper, ensuring both Parcel objects are recycled.\n *\n * This is the core of the RAII pattern for Parcels.\n * This destructor guarantees that recycle() is called, preventing resource leaks even if\n * errors occur during the transaction.\n */\nIPCBridge::ParcelWrapper::~ParcelWrapper() {\n    // Check if the parcel was successfully obtained before trying to recycle it.\n    if (data) {\n        lsplant::JNI_CallVoidMethod(env_, data.get(), bridge_->recycle_method_);\n    }\n    if (reply) {\n        lsplant::JNI_CallVoidMethod(env_, reply.get(), bridge_->recycle_method_);\n    }\n}\n\n// =========================================================================================\n// IPCBridge Implementation\n// =========================================================================================\n\nIPCBridge &IPCBridge::GetInstance() {\n    static IPCBridge instance;\n    return instance;\n}\n\nvoid IPCBridge::Initialize(JNIEnv *env) {\n    if (initialized_) {\n        return;\n    }\n\n    // --- Cache JNI Classes and Method IDs ---\n    // Caching these at startup is more efficient and robust than looking them up on every IPC call.\n    // If any of these fail, the IPC bridge is unusable.\n\n    // ServiceManager\n    auto sm_class = lsplant::ScopedLocalRef(env, env->FindClass(\"android/os/ServiceManager\"));\n    if (!sm_class) {\n        LOGE(\"IPCBridge: ServiceManager class not found!\");\n        return;\n    }\n    service_manager_class_ = (jclass)env->NewGlobalRef(sm_class.get());\n    get_service_method_ = lsplant::JNI_GetStaticMethodID(\n        env, service_manager_class_, \"getService\", \"(Ljava/lang/String;)Landroid/os/IBinder;\");\n    if (!get_service_method_) {\n        LOGE(\"IPCBridge: ServiceManager.getService method not found!\");\n        return;\n    }\n\n    // IBinder\n    auto ibinder_class = lsplant::ScopedLocalRef(env, env->FindClass(\"android/os/IBinder\"));\n    if (!ibinder_class) {\n        LOGE(\"IPCBridge: IBinder class not found!\");\n        return;\n    }\n    transact_method_ = lsplant::JNI_GetMethodID(env, ibinder_class.get(), \"transact\",\n                                                \"(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z\");\n    if (!transact_method_) {\n        LOGE(\"IPCBridge: IBinder.transact method not found!\");\n        return;\n    }\n\n    // Binder\n    auto binder_class = lsplant::ScopedLocalRef(env, env->FindClass(\"android/os/Binder\"));\n    if (!binder_class) {\n        LOGE(\"IPCBridge: Binder class not found!\");\n        return;\n    }\n    binder_class_ = (jclass)env->NewGlobalRef(binder_class.get());\n    binder_ctor_ = lsplant::JNI_GetMethodID(env, binder_class_, \"<init>\", \"()V\");\n    if (!binder_ctor_) {\n        LOGE(\"IPCBridge: Binder constructor not found!\");\n        return;\n    }\n\n    // Parcel\n    auto parcel_class = lsplant::ScopedLocalRef(env, env->FindClass(\"android/os/Parcel\"));\n    if (!parcel_class) {\n        LOGE(\"IPCBridge: Parcel class not found!\");\n        return;\n    }\n    parcel_class_ = (jclass)env->NewGlobalRef(parcel_class.get());\n    obtain_method_ =\n        lsplant::JNI_GetStaticMethodID(env, parcel_class_, \"obtain\", \"()Landroid/os/Parcel;\");\n    recycle_method_ = lsplant::JNI_GetMethodID(env, parcel_class_, \"recycle\", \"()V\");\n    write_interface_token_method_ = lsplant::JNI_GetMethodID(\n        env, parcel_class_, \"writeInterfaceToken\", \"(Ljava/lang/String;)V\");\n    write_int_method_ = lsplant::JNI_GetMethodID(env, parcel_class_, \"writeInt\", \"(I)V\");\n    write_string_method_ =\n        lsplant::JNI_GetMethodID(env, parcel_class_, \"writeString\", \"(Ljava/lang/String;)V\");\n    write_strong_binder_method_ = lsplant::JNI_GetMethodID(env, parcel_class_, \"writeStrongBinder\",\n                                                           \"(Landroid/os/IBinder;)V\");\n    read_exception_method_ = lsplant::JNI_GetMethodID(env, parcel_class_, \"readException\", \"()V\");\n    read_strong_binder_method_ =\n        lsplant::JNI_GetMethodID(env, parcel_class_, \"readStrongBinder\", \"()Landroid/os/IBinder;\");\n    read_file_descriptor_method_ = lsplant::JNI_GetMethodID(\n        env, parcel_class_, \"readFileDescriptor\", \"()Landroid/os/ParcelFileDescriptor;\");\n    read_int_method_ = lsplant::JNI_GetMethodID(env, parcel_class_, \"readInt\", \"()I\");\n    read_long_method_ = lsplant::JNI_GetMethodID(env, parcel_class_, \"readLong\", \"()J\");\n    read_string_method_ =\n        lsplant::JNI_GetMethodID(env, parcel_class_, \"readString\", \"()Ljava/lang/String;\");\n\n    // ParcelFileDescriptor\n    auto pfd_class =\n        lsplant::ScopedLocalRef(env, env->FindClass(\"android/os/ParcelFileDescriptor\"));\n    if (!pfd_class) {\n        LOGE(\"IPCBridge: ParcelFileDescriptor class not found!\");\n        return;\n    }\n    parcel_fd_class_ = (jclass)env->NewGlobalRef(pfd_class.get());\n    detach_fd_method_ = lsplant::JNI_GetMethodID(env, parcel_fd_class_, \"detachFd\", \"()I\");\n    if (!detach_fd_method_) {\n        LOGE(\"IPCBridge: ParcelFileDescriptor.detachFd method not found!\");\n        return;\n    }\n\n    LOGV(\"IPCBridge initialized successfully.\");\n    initialized_ = true;\n}\n\nlsplant::ScopedLocalRef<jobject> IPCBridge::RequestAppBinder(JNIEnv *env, jstring nice_name) {\n    if (!initialized_) {\n        LOGE(\"RequestAppBinder failed: IPCBridge not initialized.\");\n        return {env, nullptr};\n    }\n\n    // Get the rendezvous service from the Android ServiceManager.\n    auto service_name = lsplant::ScopedLocalRef(env, env->NewStringUTF(kBridgeServiceName.data()));\n    auto bridge_service = lsplant::JNI_CallStaticObjectMethod(\n        env, service_manager_class_, get_service_method_, service_name.get());\n    if (!bridge_service) {\n        LOGE(\"Could not get rendezvous service '{}'. Manager not available?\",\n             kBridgeServiceName.data());\n        return {env, nullptr};\n    }\n\n    // Prepare the IPC transaction.\n    ParcelWrapper parcels(env, this);\n    if (!parcels.data || !parcels.reply) {\n        LOGE(\"Failed to obtain parcels for IPC.\");\n        return {env, nullptr};\n    }\n\n    // This is a \"heartbeat\" binder.\n    // If our process dies, the manager service will be notified that this binder has died,\n    // allowing it to clean up resources.\n    auto heartbeat_binder =\n        lsplant::ScopedLocalRef(env, env->NewObject(binder_class_, binder_ctor_));\n    if (!heartbeat_binder) {\n        LOGE(\"Failed to create heartbeat binder.\");\n        return {env, nullptr};\n    }\n\n    // Write the request data to the 'data' parcel.\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_int_method_, kActionGetBinder);\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_string_method_, nice_name);\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_strong_binder_method_,\n                                heartbeat_binder.get());\n\n    // Perform the transaction.\n    bool success = lsplant::JNI_CallBooleanMethod(env, bridge_service.get(), transact_method_,\n                                                  kBridgeTransactionCode, parcels.data.get(),\n                                                  parcels.reply.get(), 0);\n\n    lsplant::ScopedLocalRef<jobject> result_binder = {env, nullptr};\n    if (success) {\n        // Read the reply. CRITICAL: must call readException first.\n        lsplant::JNI_CallVoidMethod(env, parcels.reply.get(), read_exception_method_);\n        if (env->ExceptionCheck()) {\n            LOGW(\"Remote exception received while requesting app binder.\");\n            env->ExceptionClear();\n        } else {\n            result_binder =\n                lsplant::JNI_CallObjectMethod(env, parcels.reply.get(), read_strong_binder_method_);\n            if (result_binder) {\n                // IMPORTANT: Keep the heartbeat binder alive by making it a global ref.\n                // If we don't do this, it gets garbage collected and the remote service\n                // thinks our process has died.\n                env->NewGlobalRef(heartbeat_binder.get());\n            }\n        }\n    } else {\n        LOGD(\"Transact call to request app binder failed.\");\n    }\n\n    return result_binder;\n}\n\nlsplant::ScopedLocalRef<jobject> IPCBridge::RequestSystemServerBinder(\n    JNIEnv *env, std::string bridgeServiceName) {\n    if (!initialized_) {\n        LOGE(\"RequestSystemServerBinder failed: IPCBridge not initialized.\");\n        return {env, nullptr};\n    }\n\n    auto service_name = lsplant::ScopedLocalRef(env, env->NewStringUTF(bridgeServiceName.data()));\n    lsplant::ScopedLocalRef<jobject> binder = {env, nullptr};\n\n    // The system_server might start its services slightly after Zygisk injects us.\n    // We retry a few times to give it a chance to register.\n    for (int i = 0; i < 3; ++i) {\n        binder = lsplant::JNI_CallStaticObjectMethod(env, service_manager_class_,\n                                                     get_service_method_, service_name.get());\n        if (binder) {\n            LOGI(\"Got system server binder via {} on attempt {}.\", bridgeServiceName.data(), i + 1);\n            return binder;\n        }\n        if (i < 2) {\n            LOGW(\"Failed to get system server binder via {}, will retry in 1 second...\",\n                 bridgeServiceName.data());\n            std::this_thread::sleep_for(std::chrono::seconds(1));\n        }\n    }\n\n    LOGE(\"Failed to get system server binder after 3 attempts. Aborting.\");\n    return {env, nullptr};\n}\n\nlsplant::ScopedLocalRef<jobject> IPCBridge::RequestManagerBinderFromSystemServer(\n    JNIEnv *env, jobject system_server_binder) {\n    ParcelWrapper parcels(env, this);\n    auto heartbeat_binder =\n        lsplant::ScopedLocalRef(env, env->NewObject(binder_class_, binder_ctor_));\n    auto system_name = lsplant::ScopedLocalRef(env, env->NewStringUTF(\"system\"));\n\n    // Populate the request\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_int_method_, getuid());\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_int_method_, getpid());\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_string_method_, system_name.get());\n    lsplant::JNI_CallVoidMethod(env, parcels.data.get(), write_strong_binder_method_,\n                                heartbeat_binder.get());\n\n    // Transact\n    bool success = lsplant::JNI_CallBooleanMethod(env, system_server_binder, transact_method_,\n                                                  kBridgeTransactionCode, parcels.data.get(),\n                                                  parcels.reply.get(), 0);\n\n    lsplant::ScopedLocalRef<jobject> result_binder = {env, nullptr};\n    if (success) {\n        lsplant::JNI_CallVoidMethod(env, parcels.reply.get(), read_exception_method_);\n        if (env->ExceptionCheck()) {\n            LOGW(\"Remote exception while getting manager binder from system_server.\");\n            env->ExceptionClear();\n        } else {\n            result_binder =\n                lsplant::JNI_CallObjectMethod(env, parcels.reply.get(), read_strong_binder_method_);\n            if (result_binder) {\n                env->NewGlobalRef(heartbeat_binder.get());  // Keep heartbeat alive\n            }\n        }\n    }\n    LOGD(\"Manager binder from system_server: {}\", static_cast<void *>(result_binder.get()));\n    return result_binder;\n}\n\nstd::tuple<int, size_t> IPCBridge::FetchFrameworkDex(JNIEnv *env, jobject binder) {\n    if (!initialized_ || !binder) {\n        return {-1, 0};\n    }\n\n    ParcelWrapper parcels(env, this);\n    bool success =\n        lsplant::JNI_CallBooleanMethod(env, binder, transact_method_, kDexTransactionCode,\n                                       parcels.data.get(), parcels.reply.get(), 0);\n\n    if (!success) {\n        LOGE(\"DEX fetch transaction failed.\");\n        return {-1, 0};\n    }\n\n    lsplant::JNI_CallVoidMethod(env, parcels.reply.get(), read_exception_method_);\n    if (env->ExceptionCheck()) {\n        LOGE(\"Remote exception received while fetching DEX.\");\n        env->ExceptionClear();\n        return {-1, 0};\n    }\n\n    auto pfd =\n        lsplant::JNI_CallObjectMethod(env, parcels.reply.get(), read_file_descriptor_method_);\n    if (!pfd) {\n        LOGE(\"Received null ParcelFileDescriptor for DEX.\");\n        return {-1, 0};\n    }\n\n    int fd = lsplant::JNI_CallIntMethod(env, pfd.get(), detach_fd_method_);\n    size_t size = static_cast<size_t>(\n        lsplant::JNI_CallLongMethod(env, parcels.reply.get(), read_long_method_));\n\n    LOGV(\"Fetched framework DEX: fd={}, size={}\", fd, size);\n    return {fd, size};\n}\n\nstd::map<std::string, std::string> IPCBridge::FetchObfuscationMap(JNIEnv *env, jobject binder) {\n    std::map<std::string, std::string> result_map;\n    if (!initialized_ || !binder) {\n        return result_map;\n    }\n\n    ParcelWrapper parcels(env, this);\n    bool success = lsplant::JNI_CallBooleanMethod(env, binder, transact_method_,\n                                                  kObfuscationMapTransactionCode,\n                                                  parcels.data.get(), parcels.reply.get(), 0);\n\n    if (!success) {\n        LOGE(\"Obfuscation map fetch transaction failed.\");\n        return result_map;\n    }\n\n    lsplant::JNI_CallVoidMethod(env, parcels.reply.get(), read_exception_method_);\n    if (env->ExceptionCheck()) {\n        LOGE(\"Remote exception received while fetching obfuscation map.\");\n        env->ExceptionClear();\n        return result_map;\n    }\n\n    int size = lsplant::JNI_CallIntMethod(env, parcels.reply.get(), read_int_method_);\n    if (size < 0 || (size % 2 != 0)) {\n        LOGE(\"Invalid size for obfuscation map received: %d\", size);\n        return result_map;\n    }\n\n    for (int i = 0; i < size / 2; ++i) {\n        auto key_jstr = lsplant::JNI_Cast<jstring>(\n            lsplant::JNI_CallObjectMethod(env, parcels.reply.get(), read_string_method_));\n\n        auto val_jstr = lsplant::JNI_Cast<jstring>(\n            lsplant::JNI_CallObjectMethod(env, parcels.reply.get(), read_string_method_));\n\n        if (env->ExceptionCheck() || !key_jstr || !val_jstr) {\n            LOGE(\"Error reading string from parcel for obfuscation map.\");\n            env->ExceptionClear();\n            result_map.clear();  // Return an empty map on error\n            return result_map;\n        }\n\n        lsplant::JUTFString key_str(env, key_jstr.get());\n        lsplant::JUTFString val_str(env, val_jstr.get());\n        result_map[key_str.get()] = val_str.get();\n    }\n\n    LOGV(\"Fetched obfuscation map with {} entries.\", result_map.size());\n    return result_map;\n}\n\njboolean IPCBridge::ExecTransact_Replace(jboolean *res, JNIEnv *env, jobject obj, va_list args) {\n    va_list copy;\n    va_copy(copy, args);\n    // Extract arguments from the va_list for Binder.execTransact(int, long, long, int)\n    auto code = va_arg(copy, jint);\n    auto data_obj = va_arg(copy, jlong);\n    auto reply_obj = va_arg(copy, jlong);\n    auto flags = va_arg(copy, jint);\n    va_end(copy);\n\n    // If the transaction code matches our special code, intercept it.\n    if (code == kBridgeTransactionCode) {\n        // Call the static Java method in our framework's BridgeService to handle the call.\n        *res = env->CallStaticBooleanMethod(GetInstance().bridge_service_class_,\n                                            GetInstance().exec_transact_replace_method_id_, obj,\n                                            code, data_obj, reply_obj, flags);\n        if (env->ExceptionCheck()) {\n            LOGW(\"Exception in Java BridgeService.execTransact handler.\");\n            env->ExceptionClear();\n        }\n        if (*res == JNI_FALSE) {\n            uint64_t caller_id = BinderCaller::GetId();\n            if (caller_id != 0) {\n                g_last_failed_id.store(caller_id, std::memory_order_relaxed);\n            }\n        }\n        return true;  // Return true to indicate we handled the call.\n    }\n    return false;  // Not our transaction, let the original method run.\n}\n\njboolean JNICALL IPCBridge::CallBooleanMethodV_Hook(JNIEnv *env, jobject obj, jmethodID methodId,\n                                                    va_list args) {\n    uint64_t current_caller_id = BinderCaller::GetId();\n    if (current_caller_id != 0) {\n        uint64_t last_failed = g_last_failed_id.load(std::memory_order_relaxed);\n        // If this caller is the one that just failed,\n        // skip interception and go straight to the original function.\n        if (current_caller_id == last_failed) {\n            // We \"consume\" the failed state by resetting it, so the *next* call is not skipped.\n            g_last_failed_id.store(~0, std::memory_order_relaxed);\n            return GetInstance().call_boolean_method_v_backup_(env, obj, methodId, args);\n        }\n    }\n\n    // Check if the method being called is the one we want to intercept: Binder.execTransact()\n    if (methodId == GetInstance().exec_transact_backup_method_id_) {\n        jboolean res = false;\n        // Attempt to handle the transaction with our replacement logic.\n        if (ExecTransact_Replace(&res, env, obj, args)) {\n            return res;  // If we handled it, return the result directly.\n        }\n        // If not handled, fall through to call the original method.\n    }\n    // Call the original, real CallBooleanMethodV function.\n    return GetInstance().call_boolean_method_v_backup_(env, obj, methodId, args);\n}\n\nvoid IPCBridge::HookBridge(JNIEnv *env) {\n    if (!initialized_) {\n        LOGE(\"Cannot hook bridge: IPCBridge is not initialized.\");\n        return;\n    }\n\n    // Get framework-specific Java classes and methods ---\n    const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();\n    std::string bridge_service_class_name;\n    bridge_service_class_name = obfs_map.at(\"org.matrix.vector.service.\") + \"BridgeService\";\n\n    auto bridge_class_ref =\n        Context::GetInstance()->FindClassFromCurrentLoader(env, bridge_service_class_name);\n    if (!bridge_class_ref) {\n        LOGE(\"Failed to find BridgeService class '{}'\", bridge_service_class_name.c_str());\n        return;\n    }\n    bridge_service_class_ = lsplant::JNI_NewGlobalRef(env, bridge_class_ref);\n\n    exec_transact_replace_method_id_ = lsplant::JNI_GetStaticMethodID(\n        env, bridge_service_class_, \"execTransact\", \"(Landroid/os/IBinder;IJJI)Z\");\n    if (!exec_transact_replace_method_id_) {\n        LOGE(\"Failed to find static method BridgeService.execTransact!\");\n        return;\n    }\n\n    // --- Prepare the JNI hook ---\n    // Get the original method ID for android.os.Binder.execTransact\n    exec_transact_backup_method_id_ =\n        lsplant::JNI_GetMethodID(env, binder_class_, \"execTransact\", \"(IJJI)Z\");\n    if (!exec_transact_backup_method_id_) {\n        LOGE(\"Failed to find original method Binder.execTransact!\");\n        return;\n    }\n\n    // Use the native library's API to get the JNI table override function.\n    auto set_table_override_func =\n        (void (*)(const JNINativeInterface *))ElfSymbolCache::GetArt()->getSymbAddress(\n            \"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface\");\n    if (!set_table_override_func) {\n        LOGE(\"Failed to find ART symbol SetTableOverride!\");\n        return;\n    }\n\n    // --- Step 3: Install the hook ---\n    // Make a full copy of the existing JNI function table.\n    memcpy(&native_interface_hook_, env->functions, sizeof(JNINativeInterface));\n\n    // Store a pointer to the original function we are about to replace.\n    call_boolean_method_v_backup_ = env->functions->CallBooleanMethodV;\n\n    // Overwrite the function pointer in our copy with our hook.\n    native_interface_hook_.CallBooleanMethodV = &CallBooleanMethodV_Hook;\n\n    // Atomically swap the process's JNI function table with our modified one.\n    set_table_override_func(&native_interface_hook_);\n\n    BinderCaller::Initialize();\n\n    LOGI(\"IPC Bridge JNI hook installed successfully.\");\n}\n\n}  // namespace vector::native::module\n"
  },
  {
    "path": "zygisk/src/main/cpp/module.cpp",
    "content": "#include <common/config.h>\n#include <common/logging.h>\n#include <core/context.h>\n#include <core/native_api.h>\n#include <elf/elf_image.h>\n#include <elf/symbol_cache.h>\n#include <jni/jni_bridge.h>\n#include <sys/system_properties.h>\n#include <unistd.h>\n\n#include <zygisk.hpp>\n\n#include \"ipc_bridge.h\"\n\nnamespace vector::native::module {\n\n// --- Process UID Constants ---\n// Values used to identify special Android processes to avoid injection.\n// https://android.googlesource.com/platform/system/core/+/master/libcutils/include/private/android_filesystem_config.h\n\n// The range of UIDs used for isolated processes (e.g., web renderers, WebView).\nconstexpr int FIRST_ISOLATED_UID = 99000;\nconstexpr int LAST_ISOLATED_UID = 99999;\n\n// The range of UIDs used for application zygotes, which are also not targets.\nconstexpr int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;\nconstexpr int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;\n\n// UID for the process responsible for creating shared RELRO files.\nconstexpr int SHARED_RELRO_UID = 1037;\n\n// Android uses this to separate users. UID = AppID + UserID * 10000.\nconstexpr int PER_USER_RANGE = 100000;\n\n// Defined via CMake generated marcos\nconstexpr uid_t kHostPackageUid = INJECTED_PACKAGE_UID;\nconst char *const kHostPackageName = INJECTED_PACKAGE_NAME;\nconst char *const kManagePackageName = MANAGER_PACKAGE_NAME;\nconstexpr uid_t GID_INET = 3003;  // Android's Internet group ID.\n\nenum RuntimeFlags : uint32_t {\n    // Flags defined by NeoZygisk\n    LATE_INJECT = 1 << 30,\n};\n\n// A simply ConfigBridge implemnetation holding obfuscation maps in memory\nusing obfuscation_map_t = std::map<std::string, std::string>;\nclass ConfigImpl : public ConfigBridge {\npublic:\n    inline static void Init() { instance_ = std::make_unique<ConfigImpl>(); }\n\n    virtual obfuscation_map_t &obfuscation_map() override { return obfuscation_map_; }\n\n    virtual void obfuscation_map(obfuscation_map_t m) override { obfuscation_map_ = std::move(m); }\n\nprivate:\n    ConfigImpl() = default;\n\n    friend std::unique_ptr<ConfigImpl> std::make_unique<ConfigImpl>();\n    obfuscation_map_t obfuscation_map_;\n};\n\n/**\n * @class VectorModule\n * @brief The core implementation of the Zygisk module for the Vector framework.\n *\n * This class is the main entry point for Zygisk. It inherits from:\n * - zygisk::ModuleBase:      To receive lifecycle callbacks from the Zygisk loader.\n * - vector::native::Context: To gain the core injection capabilities (DEX loading, ART hooking)\n *                            from the 'native' library.\n *\n * It orchestrates the injection process by deciding which processes to target,\n * using the IPCBridge to fetch the framework from the manager service, and then\n * using the Context base to perform the actual injection.\n */\nclass VectorModule : public zygisk::ModuleBase, public vector::native::Context {\npublic:\n    void onLoad(zygisk::Api *api, JNIEnv *env) override;\n    void preAppSpecialize(zygisk::AppSpecializeArgs *args) override;\n    void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override;\n    void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override;\n    void postServerSpecialize(const zygisk::ServerSpecializeArgs *args) override;\n\nprotected:\n    /**\n     * @brief Provides the concrete implementation for loading the framework DEX.\n     *\n     * This method is a pure virtual in the native::core::Context base class and\n     * must be implemented here.\n     * It uses an InMemoryDexClassLoader to load our framework into the target process.\n     */\n    void LoadDex(JNIEnv *env, PreloadedDex &&dex) override;\n\n    /**\n     * @brief Provides the concrete implementation for finding the Java entry\n     * class.\n     *\n     * This method is also a pure virtual in the base class.\n     * It uses the obfuscation map to determine the real entry class name and\n     * finds it in the ClassLoader we created in LoadDex.\n     */\n    void SetupEntryClass(JNIEnv *env) override;\n\nprivate:\n    /**\n     * @brief Encapsulates the logic for telling Zygisk whether to unload our library.\n     *\n     * If we don't inject into a process, we allow Zygisk to dlclose our .so.\n     * Otherwise, we MUST prevent this.\n     * @param unload True to allow unloading, false to prevent it.\n     */\n    void SetAllowUnload(bool unload);\n\n    zygisk::Api *api_ = nullptr;\n    JNIEnv *env_ = nullptr;\n\n    // --- ART Hooker Configuration ---\n    const lsplant::InitInfo init_info_{\n        .inline_hooker =\n            [](auto target, auto replace) {\n                void *backup = nullptr;\n                return HookInline(target, replace, &backup) == 0 ? backup : nullptr;\n            },\n        .inline_unhooker = [](auto target) { return UnhookInline(target) == 0; },\n        .art_symbol_resolver =\n            [](auto symbol) { return ElfSymbolCache::GetArt()->getSymbAddress(symbol); },\n        .art_symbol_prefix_resolver =\n            [](auto symbol) { return ElfSymbolCache::GetArt()->getSymbPrefixFirstAddress(symbol); },\n    };\n\n    // State managed within the class instance for each forked process.\n    bool should_inject_ = false;\n    bool is_manager_app_ = false;\n};\n\n// =========================================================================================\n// Implementation of VectorModule\n// =========================================================================================\n\nvoid VectorModule::LoadDex(JNIEnv *env, PreloadedDex &&dex) {\n    LOGV(\"Loading framework DEX into memory (size: {}).\", dex.size());\n\n    // Get the system ClassLoader. This will be the parent of our new loader.\n    auto classloader_class = lsplant::JNI_FindClass(env, \"java/lang/ClassLoader\");\n    if (!classloader_class) {\n        LOGE(\"Failed to find java.lang.ClassLoader\");\n        return;\n    }\n    auto getsyscl_mid = lsplant::JNI_GetStaticMethodID(\n        env, classloader_class.get(), \"getSystemClassLoader\", \"()Ljava/lang/ClassLoader;\");\n    auto system_classloader =\n        lsplant::JNI_CallStaticObjectMethod(env, classloader_class.get(), getsyscl_mid);\n    if (!system_classloader) {\n        LOGE(\"Failed to get SystemClassLoader\");\n        return;\n    }\n\n    // Create a Java ByteBuffer wrapping our in-memory DEX data.\n    auto byte_buffer_class = lsplant::JNI_FindClass(env, \"java/nio/ByteBuffer\");\n    if (!byte_buffer_class) {\n        LOGE(\"Failed to find java.nio.ByteBuffer\");\n        return;\n    }\n    auto dex_buffer =\n        lsplant::ScopedLocalRef(env, env->NewDirectByteBuffer(dex.data(), dex.size()));\n    if (!dex_buffer) {\n        LOGE(\"Failed to create DirectByteBuffer for DEX.\");\n        return;\n    }\n\n    // Create an InMemoryDexClassLoader instance.\n    auto in_memory_cl_class = lsplant::JNI_FindClass(env, \"dalvik/system/InMemoryDexClassLoader\");\n    if (!in_memory_cl_class) {\n        LOGE(\"Failed to find InMemoryDexClassLoader.\");\n        return;\n    }\n    auto init_mid = lsplant::JNI_GetMethodID(env, in_memory_cl_class.get(), \"<init>\",\n                                             \"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V\");\n    if (!init_mid) {\n        LOGE(\"Failed to find InMemoryDexClassLoader constructor.\");\n        return;\n    }\n\n    auto new_cl =\n        lsplant::ScopedLocalRef(env, env->NewObject(in_memory_cl_class.get(), init_mid,\n                                                    dex_buffer.get(), system_classloader.get()));\n    if (env->ExceptionCheck() || !new_cl) {\n        LOGE(\"Failed to create InMemoryDexClassLoader instance.\");\n        env->ExceptionClear();\n        return;\n    }\n\n    // Store a global reference to our new ClassLoader.\n    inject_class_loader_ = env->NewGlobalRef(new_cl.get());\n    LOGV(\"Framework ClassLoader created successfully.\");\n}\n\nvoid VectorModule::SetupEntryClass(JNIEnv *env) {\n    if (!inject_class_loader_) {\n        LOGE(\"Cannot setup entry class: ClassLoader is null.\");\n        return;\n    }\n\n    // Use the obfuscation map from the config to get the real class name.\n    const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();\n    std::string entry_class_name;\n    entry_class_name = obfs_map.at(\"org.matrix.vector.core.\") + \"Main\";\n\n    // We must find the class through our custom ClassLoader.\n    auto entry_class = this->FindClassFromLoader(env, inject_class_loader_, entry_class_name);\n    if (!entry_class) {\n        LOGE(\"Failed to find entry class '{}' in the loaded DEX.\", entry_class_name.c_str());\n        return;\n    }\n\n    // Store a global reference to the entry class.\n    entry_class_ = lsplant::JNI_NewGlobalRef(env, entry_class);\n    LOGV(\"Framework entry class '{}' located.\", entry_class_name.c_str());\n}\n\nvoid VectorModule::onLoad(zygisk::Api *api, JNIEnv *env) {\n    this->api_ = api;\n    this->env_ = env;\n\n    // Create two singlton instances for classes Context and ConfigBridge\n    instance_.reset(this);\n    ConfigImpl::Init();\n    LOGD(\"Vector Zygisk module loaded\");\n}\n\nvoid VectorModule::preAppSpecialize(zygisk::AppSpecializeArgs *args) {\n    // Reset state for this new process fork.\n    should_inject_ = false;\n    is_manager_app_ = false;\n\n    // --- Manager App Special Handling ---\n    // We identify our manager app by a special UID and\n    // grant it internet permissions by adding it to the INET group.\n    if (args->uid == kHostPackageUid) {\n        lsplant::JUTFString nice_name_str(env_, args->nice_name);\n        if (nice_name_str.get() == std::string(kManagePackageName)) {\n            LOGI(\"Manager app detected. Granting internet permissions.\");\n            is_manager_app_ = true;\n\n            // Add GID_INET to the GID list.\n            int original_gids_count = env_->GetArrayLength(args->gids);\n            jintArray new_gids = env_->NewIntArray(original_gids_count + 1);\n            if (env_->ExceptionCheck()) {\n                LOGE(\"Failed to create new GID array for manager.\");\n                env_->ExceptionClear();  // Clear exception to prevent a crash.\n                return;\n            }\n\n            jint *gids_array = env_->GetIntArrayElements(args->gids, nullptr);\n            env_->SetIntArrayRegion(new_gids, 0, original_gids_count, gids_array);\n            env_->ReleaseIntArrayElements(args->gids, gids_array, JNI_ABORT);\n\n            jint inet_gid = GID_INET;\n            env_->SetIntArrayRegion(new_gids, original_gids_count, 1, &inet_gid);\n\n            args->nice_name = env_->NewStringUTF(INJECTED_PACKAGE_NAME);\n            args->gids = new_gids;\n        }\n    }\n\n    IPCBridge::GetInstance().Initialize(env_);\n\n    // --- Injection Decision Logic ---\n    // Determine if the current process is a valid target for injection.\n    lsplant::JUTFString nice_name_str(env_, args->nice_name);\n\n    // An app without a data directory cannot be a target.\n    if (!args->app_data_dir) {\n        LOGD(\"Skipping injection for '{}': no app_data_dir.\", nice_name_str.get());\n        return;\n    }\n\n    // Child Zygotes are specialized zygotes for apps like WebView and are not targets.\n    if (args->is_child_zygote && *args->is_child_zygote) {\n        LOGD(\"Skipping injection for '{}': is a child zygote.\", nice_name_str.get());\n        return;\n    }\n\n    // Skip isolated processes, which are heavily sandboxed.\n    const uid_t app_id = args->uid % PER_USER_RANGE;\n    if ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) ||\n        (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) ||\n        app_id == SHARED_RELRO_UID) {\n        LOGV(\"Skipping injection for '{}': is an isolated process (UID: %d).\", nice_name_str.get(),\n             app_id);\n        return;\n    }\n\n    // If we passed all checks, mark this process for injection.\n    should_inject_ = true;\n    LOGV(\"Process '{}' (UID: {}) is marked for injection.\", nice_name_str.get(), args->uid);\n}\n\nvoid VectorModule::postAppSpecialize(const zygisk::AppSpecializeArgs *args) {\n    if (!should_inject_) {\n        SetAllowUnload(true);  // Not a target, allow module to be unloaded.\n        return;\n    }\n\n    if (is_manager_app_) {\n        args->nice_name = env_->NewStringUTF(kManagePackageName);\n    }\n\n    // --- Framework Injection ---\n    lsplant::JUTFString nice_name_str(env_, args->nice_name);\n    LOGD(\"Attempting injection into '{}'.\", nice_name_str.get());\n\n    auto &ipc_bridge = IPCBridge::GetInstance();\n    auto binder = ipc_bridge.RequestAppBinder(env_, args->nice_name);\n    if (!binder) {\n        LOGD(\"No IPC binder obtained for '{}'. Skipping injection.\", nice_name_str.get());\n        SetAllowUnload(true);\n        return;\n    }\n\n    // Fetch resources from the manager service.\n    auto [dex_fd, dex_size] = ipc_bridge.FetchFrameworkDex(env_, binder.get());\n    if (dex_fd < 0) {\n        LOGE(\"Failed to fetch framework DEX for '{}'.\", nice_name_str.get());\n        SetAllowUnload(true);\n        return;\n    }\n\n    auto obfs_map = ipc_bridge.FetchObfuscationMap(env_, binder.get());\n    ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map));\n\n    {\n        PreloadedDex dex(dex_fd, dex_size);\n        this->LoadDex(env_, std::move(dex));\n    }\n    close(dex_fd);  // The FD is duplicated by mmap, we can close it now.\n\n    // Initialize ART hooks via the native library.\n    this->InitArtHooker(env_, init_info_);\n    // Initialize JNI hooks via the native library.\n    this->InitHooks(env_);\n    // Find the Java entrypoint.\n    this->SetupEntryClass(env_);\n\n    // Hand off control to the Java side of the framework.\n    this->FindAndCall(\n        env_, \"forkCommon\", \"(ZZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V\",\n        JNI_FALSE, JNI_FALSE, args->nice_name, args->app_data_dir, binder.get(), is_manager_app_);\n\n    LOGV(\"Injected Vector framework into '{}'.\", nice_name_str.get());\n    SetAllowUnload(false);  // We are injected, PREVENT module unloading.\n}\n\nvoid VectorModule::preServerSpecialize(zygisk::ServerSpecializeArgs *args) {\n    // The system server is always a target for injection.\n    should_inject_ = true;\n    LOGI(\"System server process detected. Marking for injection.\");\n\n    // Initialize our IPC bridge singleton.\n    IPCBridge::GetInstance().Initialize(env_);\n}\n\nvoid VectorModule::postServerSpecialize(const zygisk::ServerSpecializeArgs *args) {\n    if (!should_inject_) {\n        SetAllowUnload(true);\n        return;\n    }\n\n    LOGD(\"Attempting injection into system_server.\");\n\n    // --- Device-Specific Workaround ---\n    // Some ZTE devices require argv[0] to be explicitly set to \"system_server\"\n    // for certain services to function correctly after modification.\n    if (__system_property_find(\"ro.vendor.product.ztename\")) {\n        LOGV(\"Applying ZTE-specific workaround: setting argv[0] to system_server.\");\n        auto process_class = lsplant::ScopedLocalRef(env_, env_->FindClass(\"android/os/Process\"));\n        if (process_class) {\n            auto set_argv0_mid =\n                env_->GetStaticMethodID(process_class.get(), \"setArgV0\", \"(Ljava/lang/String;)V\");\n            auto name_str = lsplant::ScopedLocalRef(env_, env_->NewStringUTF(\"system_server\"));\n            if (set_argv0_mid && name_str) {\n                env_->CallStaticVoidMethod(process_class.get(), set_argv0_mid, name_str.get());\n            }\n        }\n        if (env_->ExceptionCheck()) {\n            LOGW(\"Exception occurred during ZTE workaround.\");\n            env_->ExceptionClear();\n        }\n    }\n\n    // --- Framework Injection for System Server ---\n    auto &ipc_bridge = IPCBridge::GetInstance();\n    std::string bridgeServiceName = \"serial\";\n    bool is_late_inject = (args->runtime_flags & RuntimeFlags::LATE_INJECT) != 0;\n    if (is_late_inject) bridgeServiceName = \"serial_vector\";\n    auto system_binder = ipc_bridge.RequestSystemServerBinder(env_, bridgeServiceName);\n    if (!system_binder) {\n        LOGE(\"Failed to get system server IPC binder. Aborting injection.\");\n        SetAllowUnload(true);  // Allow unload on failure.\n        return;\n    }\n\n    auto manager_binder =\n        ipc_bridge.RequestManagerBinderFromSystemServer(env_, system_binder.get());\n\n    // Use either the direct manager binder if available,\n    // otherwise proxy through the system binder.\n    jobject effective_binder = manager_binder ? manager_binder.get() : system_binder.get();\n\n    auto [dex_fd, dex_size] = ipc_bridge.FetchFrameworkDex(env_, effective_binder);\n    if (dex_fd < 0) {\n        LOGE(\"Failed to fetch framework DEX for system_server.\");\n        SetAllowUnload(true);\n        return;\n    }\n\n    auto obfs_map = ipc_bridge.FetchObfuscationMap(env_, effective_binder);\n    ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map));\n\n    {\n        PreloadedDex dex(dex_fd, dex_size);\n        this->LoadDex(env_, std::move(dex));\n    }\n    close(dex_fd);\n\n    ipc_bridge.HookBridge(env_);\n\n    this->InitArtHooker(env_, init_info_);\n    this->InitHooks(env_);\n    this->SetupEntryClass(env_);\n\n    auto system_name = lsplant::ScopedLocalRef(env_, env_->NewStringUTF(\"system\"));\n    this->FindAndCall(env_, \"forkCommon\",\n                      \"(ZZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V\", JNI_TRUE,\n                      is_late_inject, system_name.get(), nullptr, manager_binder.get(),\n                      is_manager_app_);\n\n    LOGI(\"Injected Vector framework into system_server.\");\n    SetAllowUnload(false);  // We are injected, PREVENT module unloading.\n}\n\nvoid VectorModule::SetAllowUnload(bool unload) {\n    if (api_ && unload) {\n        LOGD(\"Allowing Zygisk to unload module library.\");\n        api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);\n\n        // Release the pointer from the unique_ptr's control. This prevents the\n        // static unique_ptr's destructor from calling delete on our object, which\n        // would cause a double-free when the Zygisk framework cleans up.\n        if (instance_.release() != nullptr) {\n            LOGD(\"Module context singleton released.\");\n        }\n    } else {\n        LOGD(\"Preventing Zygisk from unloading module library.\");\n    }\n}\n\n}  // namespace vector::native::module\n\n// =========================================================================================\n// Zygisk Module Registration\n// =========================================================================================\nREGISTER_ZYGISK_MODULE(vector::native::module::VectorModule);\n"
  },
  {
    "path": "zygisk/src/main/kotlin/org/matrix/vector/ParasiticManagerHooker.kt",
    "content": "package org.matrix.vector\n\nimport android.annotation.SuppressLint\nimport android.app.ActivityThread\nimport android.app.LoadedApk\nimport android.content.BroadcastReceiver\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.*\nimport android.os.*\nimport android.util.AndroidRuntimeException\nimport android.util.ArrayMap\nimport android.webkit.WebViewDelegate\nimport android.webkit.WebViewFactory\nimport de.robv.android.xposed.XC_MethodHook\nimport de.robv.android.xposed.XC_MethodReplacement\nimport de.robv.android.xposed.XposedBridge\nimport de.robv.android.xposed.XposedHelpers\nimport hidden.HiddenApiBridge\nimport java.io.FileInputStream\nimport java.io.FileOutputStream\nimport java.lang.reflect.Method\nimport java.util.concurrent.ConcurrentHashMap\nimport org.lsposed.lspd.ILSPManagerService\nimport org.lsposed.lspd.core.ApplicationServiceClient.serviceClient\nimport org.lsposed.lspd.util.Hookers\nimport org.lsposed.lspd.util.Utils\n\n/** The \"Parasite\" logic. Injects the LSPosed Manager APK into a host process (shell). */\n@SuppressLint(\"StaticFieldLeak\")\nobject ParasiticManagerHooker {\n    private const val CHROMIUM_WEBVIEW_FACTORY_METHOD = \"create\"\n\n    private var managerPkgInfo: PackageInfo? = null\n    private var managerFd: Int = -1\n\n    // Manually track Activity states since the system is unaware of our spoofed activities\n    private val states = ConcurrentHashMap<String, Bundle>()\n    private val persistentStates = ConcurrentHashMap<String, PersistableBundle>()\n\n    /** Constructs a hybrid PackageInfo. Combines the Manager's code with the Host's environment. */\n    @Synchronized\n    private fun getManagerPkgInfo(appInfo: ApplicationInfo?): PackageInfo? {\n        if (managerPkgInfo == null && appInfo != null) {\n            runCatching {\n                    val ctx: Context = ActivityThread.currentActivityThread().systemContext\n                    var sourcePath = \"/proc/self/fd/$managerFd\"\n\n                    // SDK <= 28 (Android 9) cannot reliably parse APKs via FD paths in all\n                    // contexts.\n                    // We copy the APK to the host's cache as a workaround.\n                    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {\n                        val dstPath = \"${appInfo.dataDir}/cache/lsposed.apk\"\n                        runCatching {\n                                FileInputStream(sourcePath).use { input ->\n                                    FileOutputStream(dstPath).use { output ->\n                                        input.channel.transferTo(\n                                            0,\n                                            input.channel.size(),\n                                            output.channel,\n                                        )\n                                    }\n                                }\n                                sourcePath = dstPath\n                            }\n                            .onFailure { Hookers.logE(\"Failed to copy parasitic APK\", it) }\n                    }\n\n                    val pkgInfo =\n                        ctx.packageManager.getPackageArchiveInfo(\n                            sourcePath,\n                            PackageManager.GET_ACTIVITIES,\n                        ) ?: throw RuntimeException(\"PackageManager failed to parse $sourcePath\")\n\n                    // Transplant identity: Keep host's paths and UID, swap the code source\n                    pkgInfo.applicationInfo!!.apply {\n                        sourceDir = sourcePath\n                        publicSourceDir = sourcePath\n                        nativeLibraryDir = appInfo.nativeLibraryDir\n                        packageName = appInfo.packageName\n                        dataDir =\n                            HiddenApiBridge.ApplicationInfo_credentialProtectedDataDir(appInfo)\n                        deviceProtectedDataDir = appInfo.deviceProtectedDataDir\n                        processName = appInfo.processName\n                        uid = appInfo.uid\n                        // A14 QPR3 Fix: Ensure the flag for code existence is set\n                        flags = flags or ApplicationInfo.FLAG_HAS_CODE\n\n                        HiddenApiBridge.ApplicationInfo_credentialProtectedDataDir(\n                            this,\n                            HiddenApiBridge.ApplicationInfo_credentialProtectedDataDir(appInfo),\n                        )\n                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n                            HiddenApiBridge.ApplicationInfo_overlayPaths(\n                                this,\n                                HiddenApiBridge.ApplicationInfo_overlayPaths(appInfo),\n                            )\n                        }\n                        HiddenApiBridge.ApplicationInfo_resourceDirs(\n                            this,\n                            HiddenApiBridge.ApplicationInfo_resourceDirs(appInfo),\n                        )\n                    }\n                    managerPkgInfo = pkgInfo\n                }\n                .onFailure { Utils.logE(\"Failed to construct manager PkgInfo\", it) }\n        }\n        return managerPkgInfo\n    }\n\n    /**\n     * Passes the IPC binder to the Manager's internal [Constants] class so it can communicate back\n     * to the LSPosed system service.\n     */\n    private fun sendBinderToManager(classLoader: ClassLoader, binder: IBinder) {\n        runCatching {\n                val clazz =\n                    XposedHelpers.findClass(\n                        BuildConfig.ManagerPackageName + \".Constants\",\n                        classLoader,\n                    )\n                val ok =\n                    XposedHelpers.callStaticMethod(\n                        clazz,\n                        \"setBinder\",\n                        arrayOf(IBinder::class.java),\n                        binder,\n                    ) as Boolean\n                if (!ok) throw RuntimeException(\"setBinder returned false\")\n            }\n            .onFailure { Utils.logW(\"Could not send binder to LSPosed Manager\", it) }\n    }\n\n    private fun hookForManager(managerService: ILSPManagerService) {\n        // Hook 1: Swap ApplicationInfo during host binding\n        XposedHelpers.findAndHookMethod(\n            ActivityThread::class.java,\n            \"handleBindApplication\",\n            \"android.app.ActivityThread\\$AppBindData\",\n            object : XC_MethodHook() {\n                override fun beforeHookedMethod(param: MethodHookParam<*>) {\n                    Hookers.logD(\"ActivityThread#handleBindApplication() starts\")\n                    val bindData = param.args[0]\n                    val hostAppInfo =\n                        XposedHelpers.getObjectField(bindData, \"appInfo\") as ApplicationInfo\n                    val parasiticInfo = getManagerPkgInfo(hostAppInfo)?.applicationInfo\n                    XposedHelpers.setObjectField(bindData, \"appInfo\", parasiticInfo)\n                }\n            },\n        )\n\n        // Hook 2: Inject APK path into the ClassLoader\n        var classLoaderUnhook: XC_MethodHook.Unhook? = null\n        val classLoaderHook =\n            object : XC_MethodHook() {\n                override fun afterHookedMethod(param: MethodHookParam<*>) {\n                    val pkgInfo = getManagerPkgInfo(null) ?: return\n                    val mAppInfo =\n                        XposedHelpers.getObjectField(param.thisObject, \"mApplicationInfo\")\n\n                    val managerAppInfo = pkgInfo.applicationInfo!!\n\n                    if (mAppInfo == managerAppInfo) {\n                        val dexPath = managerAppInfo.sourceDir\n                        val pathClassLoader = param.result as ClassLoader\n\n                        Hookers.logD(\"Injecting DEX into LoadedApk ClassLoader: $pathClassLoader\")\n                        val pathList = XposedHelpers.getObjectField(pathClassLoader, \"pathList\")\n                        val dexPaths = XposedHelpers.callMethod(pathList, \"getDexPaths\") as List<*>\n\n                        if (!dexPaths.contains(dexPath)) {\n                            Utils.logW(\"Manager APK not found in ClassLoader, adding manually...\")\n                            XposedHelpers.callMethod(pathClassLoader, \"addDexPath\", dexPath)\n                        }\n                        sendBinderToManager(pathClassLoader, managerService.asBinder())\n                        classLoaderUnhook!!.unhook() // Only need to inject once\n                    }\n                }\n            }\n        classLoaderUnhook =\n            XposedHelpers.findAndHookMethod(\n                LoadedApk::class.java,\n                \"getClassLoader\",\n                classLoaderHook,\n            )\n\n        // Hook 3: Activity Lifecycle & Intent Redirection\n        val activityClientRecordClass =\n            XposedHelpers.findClass(\n                \"android.app.ActivityThread\\$ActivityClientRecord\",\n                ActivityThread::class.java.classLoader,\n            )\n        val activityHooker =\n            object : XC_MethodHook() {\n                override fun beforeHookedMethod(param: MethodHookParam<*>) {\n                    param.args.forEachIndexed { i, arg ->\n                        if (arg is ActivityInfo) {\n                            val pkgInfo =\n                                getManagerPkgInfo(arg.applicationInfo) ?: return@forEachIndexed\n                            pkgInfo.activities\n                                ?.find {\n                                    it.name ==\n                                        BuildConfig.ManagerPackageName + \".ui.activity.MainActivity\"\n                                }\n                                ?.let {\n                                    it.applicationInfo = pkgInfo.applicationInfo\n                                    param.args[i] = it\n                                }\n                        }\n                        if (arg is Intent) {\n                            arg.component =\n                                ComponentName(\n                                    arg.component!!.packageName,\n                                    BuildConfig.ManagerPackageName + \".ui.activity.MainActivity\",\n                                )\n                        }\n                    }\n\n                    // Captured State Injection\n                    if (param.method.getName() == \"scheduleLaunchActivity\") {\n                        var currentAInfo: ActivityInfo? = null\n                        val types = (param.method as Method).parameterTypes\n                        types.forEachIndexed { idx, type ->\n                            when (type) {\n                                ActivityInfo::class.java ->\n                                    currentAInfo = param.args[idx] as ActivityInfo\n                                Bundle::class.java ->\n                                    currentAInfo?.let { info ->\n                                        states[info.name]?.let { param.args[idx] = it }\n                                    }\n                                PersistableBundle::class.java ->\n                                    currentAInfo?.let { info ->\n                                        persistentStates[info.name]?.let { param.args[idx] = it }\n                                    }\n                            }\n                        }\n                    }\n                }\n\n                override fun afterHookedMethod(param: MethodHookParam<*>) {\n                    if (!activityClientRecordClass.isInstance(param.thisObject)) return\n                    param.args.filterIsInstance<ActivityInfo>().forEach { aInfo ->\n                        Hookers.logD(\"Restoring state for Activity: ${aInfo.name}\")\n                        states[aInfo.name]?.let {\n                            XposedHelpers.setObjectField(param.thisObject, \"state\", it)\n                        }\n                        persistentStates[aInfo.name]?.let {\n                            XposedHelpers.setObjectField(param.thisObject, \"persistentState\", it)\n                        }\n                    }\n                }\n            }\n\n        XposedBridge.hookAllConstructors(activityClientRecordClass, activityHooker)\n        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {\n            val appThreadClass =\n                XposedHelpers.findClass(\n                    \"android.app.ActivityThread\\$ApplicationThread\",\n                    ActivityThread::class.java.classLoader,\n                )\n            XposedBridge.hookAllMethods(appThreadClass, \"scheduleLaunchActivity\", activityHooker)\n        }\n\n        // Hook 4: Ignore Receivers (Manager doesn't need to handle host receivers)\n        XposedBridge.hookAllMethods(\n            ActivityThread::class.java,\n            \"handleReceiver\",\n            object : XC_MethodReplacement() {\n                override fun replaceHookedMethod(param: MethodHookParam<*>): Any? {\n                    param.args.filterIsInstance<BroadcastReceiver.PendingResult>().forEach {\n                        it.finish()\n                    }\n                    return null\n                }\n            },\n        )\n\n        // Hook 5: Provider Context Spoofing\n        XposedBridge.hookAllMethods(\n            ActivityThread::class.java,\n            \"installProvider\",\n            object : XC_MethodHook() {\n                private var originalContext: Context? = null\n\n                override fun beforeHookedMethod(param: MethodHookParam<*>) {\n                    var ctx: Context? = null\n                    var info: ProviderInfo? = null\n                    var ctxIdx = -1\n\n                    param.args.forEachIndexed { i, arg ->\n                        when (arg) {\n                            is Context -> {\n                                ctx = arg\n                                ctxIdx = i\n                            }\n                            is ProviderInfo -> info = arg\n                        }\n                    }\n\n                    val pkgInfo = getManagerPkgInfo(null)\n                    if (ctx != null && info != null && pkgInfo != null) {\n                        val managerPackage = pkgInfo.applicationInfo!!.packageName\n                        if (info.applicationInfo.packageName != managerPackage) return\n\n                        if (originalContext == null) {\n                            // Create a fake original context to satisfy internal package checks\n                            info.applicationInfo.packageName = \"$managerPackage.origin\"\n                            val compatibilityInfo =\n                                HiddenApiBridge.Resources_getCompatibilityInfo(ctx!!.resources)\n                            val originalPkgInfo =\n                                ActivityThread.currentActivityThread()\n                                    .getPackageInfoNoCheck(info.applicationInfo, compatibilityInfo)\n                            XposedHelpers.setObjectField(\n                                originalPkgInfo,\n                                \"mPackageName\",\n                                managerPackage,\n                            )\n\n                            val contextImplClass =\n                                XposedHelpers.findClass(\"android.app.ContextImpl\", null)\n                            originalContext =\n                                XposedHelpers.callStaticMethod(\n                                    contextImplClass,\n                                    \"createAppContext\",\n                                    ActivityThread.currentActivityThread(),\n                                    originalPkgInfo,\n                                ) as Context\n                            info.applicationInfo.packageName = managerPackage\n                        }\n                        param.args[ctxIdx] = originalContext\n                    }\n                }\n            },\n        )\n\n        // Hook 6: WebView initialization within Parasitic process\n        XposedHelpers.findAndHookMethod(\n            WebViewFactory::class.java,\n            \"getProvider\",\n            object : XC_MethodReplacement() {\n                override fun replaceHookedMethod(param: MethodHookParam<*>): Any? {\n                    val existing =\n                        XposedHelpers.getStaticObjectField(\n                            WebViewFactory::class.java,\n                            \"sProviderInstance\",\n                        )\n                    if (existing != null) return existing\n\n                    val providerClass =\n                        XposedHelpers.callStaticMethod(\n                            WebViewFactory::class.java,\n                            \"getProviderClass\",\n                        ) as Class<*>\n                    return try {\n                        val staticFactory =\n                            providerClass.getMethod(\n                                CHROMIUM_WEBVIEW_FACTORY_METHOD,\n                                WebViewDelegate::class.java,\n                            )\n                        val delegateCtor =\n                            WebViewDelegate::class.java.getDeclaredConstructor().apply {\n                                isAccessible = true\n                            }\n                        val instance = staticFactory.invoke(null, delegateCtor.newInstance())\n                        XposedHelpers.setStaticObjectField(\n                            WebViewFactory::class.java,\n                            \"sProviderInstance\",\n                            instance,\n                        )\n                        Hookers.logD(\"WebView provider initialized: $instance\")\n                        instance\n                    } catch (e: Exception) {\n                        Hookers.logE(\"WebView initialization failed\", e)\n                        throw AndroidRuntimeException(e)\n                    }\n                }\n            },\n        )\n\n        // Hook 7: State Capture on Stop\n        val stateCaptureHooker =\n            object : XC_MethodHook() {\n                override fun beforeHookedMethod(param: MethodHookParam<*>) {\n                    runCatching {\n                            var record = param.args[0]\n                            if (record is IBinder) {\n                                val activities =\n                                    XposedHelpers.getObjectField(param.thisObject, \"mActivities\")\n                                        as ArrayMap<*, *>\n                                record = activities[record] ?: return\n                            }\n\n                            val saveMethod =\n                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)\n                                    \"callActivityOnSaveInstanceState\"\n                                else \"callCallActivityOnSaveInstanceState\"\n                            XposedHelpers.callMethod(param.thisObject, saveMethod, record)\n\n                            val state = XposedHelpers.getObjectField(record, \"state\") as? Bundle\n                            val pState =\n                                XposedHelpers.getObjectField(record, \"persistentState\")\n                                    as? PersistableBundle\n                            val aInfo =\n                                XposedHelpers.getObjectField(record, \"activityInfo\") as ActivityInfo\n\n                            state?.let { states[aInfo.name] = it }\n                            pState?.let { persistentStates[aInfo.name] = it }\n                            Hookers.logD(\"Saved state for ${aInfo.name}\")\n                        }\n                        .onFailure { Hookers.logE(\"Failed to save activity state\", it) }\n                }\n            }\n        XposedBridge.hookAllMethods(\n            ActivityThread::class.java,\n            \"performStopActivityInner\",\n            stateCaptureHooker,\n        )\n        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {\n            XposedHelpers.findAndHookMethod(\n                ActivityThread::class.java,\n                \"performDestroyActivity\",\n                IBinder::class.java,\n                Boolean::class.javaPrimitiveType,\n                Int::class.javaPrimitiveType,\n                Boolean::class.javaPrimitiveType,\n                stateCaptureHooker,\n            )\n        }\n    }\n\n    /** Entry point. Checks if the current process should host the parasitic manager. */\n    @JvmStatic\n    fun start(): Boolean {\n        val binderList = mutableListOf<IBinder>()\n        return try {\n            serviceClient.requestInjectedManagerBinder(binderList).use { pfd ->\n                managerFd = pfd.detachFd()\n                val managerService = ILSPManagerService.Stub.asInterface(binderList[0])\n                hookForManager(managerService)\n                Utils.logD(\"Vector manager injected successfully into process.\")\n                true\n            }\n        } catch (e: Throwable) {\n            Utils.logE(\"Parasitic injection failed\", e)\n            false\n        }\n    }\n}\n"
  },
  {
    "path": "zygisk/src/main/kotlin/org/matrix/vector/ParasiticManagerSystemHooker.kt",
    "content": "package org.matrix.vector\n\nimport android.annotation.SuppressLint\nimport android.app.ProfilerInfo\nimport android.content.Intent\nimport android.content.pm.ActivityInfo\nimport android.content.pm.ResolveInfo\nimport io.github.libxposed.api.XposedInterface\nimport org.lsposed.lspd.hooker.HandleSystemServerProcessHooker\nimport org.lsposed.lspd.impl.LSPosedHelper\nimport org.lsposed.lspd.util.Utils\nimport org.matrix.vector.service.BridgeService\n\n/**\n * Handles System-Server side logic for the Parasitic Manager.\n *\n * When a user tries to open the LSPosed Manager, the system normally wouldn't know how to handle it\n * because it isn't \"installed.\" This class intercepts the activity resolution and tells the system\n * to launch it in a special process.\n */\nclass ParasiticManagerSystemHooker : HandleSystemServerProcessHooker.Callback {\n\n    companion object {\n        @JvmStatic\n        fun start() {\n            // Register this class as the handler for system_server initialization\n            HandleSystemServerProcessHooker.callback = ParasiticManagerSystemHooker()\n        }\n    }\n\n    /** Intercepts Activity resolution in the System Server. */\n    object Hooker : XposedInterface.Hooker {\n        @JvmStatic\n        fun after(callback: XposedInterface.AfterHookCallback) {\n            val intent = callback.args[0] as? Intent ?: return\n\n            // Check if this intent is meant for the LSPosed Manager\n            if (!intent.hasCategory(BuildConfig.ManagerPackageName + \".LAUNCH_MANAGER\")) return\n\n            val result = callback.result as? ActivityInfo ?: return\n\n            // We only intercept if it's currently resolving to the shell/fallback\n            if (result.packageName != BuildConfig.InjectedPackageName) return\n\n            // --- Redirection Logic ---\n            // We create a copy of the ActivityInfo to avoid polluting the system's cache.\n            val redirectedInfo =\n                ActivityInfo(result).apply {\n                    // Force the manager to run in its own dedicated process name\n                    processName = BuildConfig.ManagerPackageName\n\n                    // Set a standard theme so transition animations work correctly\n                    theme = android.R.style.Theme_DeviceDefault_Settings\n\n                    // Ensure the activity isn't excluded from recents by host flags\n                    flags =\n                        flags and\n                            (ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS or\n                                    ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS)\n                                .inv()\n                }\n\n            // Notify the bridge service that we are about to start the manager\n            BridgeService.getService()?.preStartManager()\n\n            // Replace the original ResolveInfo with our parasitic one\n            callback.result = redirectedInfo\n        }\n    }\n\n    @SuppressLint(\"PrivateApi\")\n    override fun onSystemServerLoaded(classLoader: ClassLoader) {\n        runCatching {\n                // Android versions change the name of the internal class responsible for activity\n                // tracking.\n                // We check the most likely candidates based on API levels (9.0 through 14+).\n                val supervisorClass =\n                    try {\n                        // Android 12.0 - 14+\n                        Class.forName(\n                            \"com.android.server.wm.ActivityTaskSupervisor\",\n                            false,\n                            classLoader,\n                        )\n                    } catch (e: ClassNotFoundException) {\n                        try {\n                            // Android 10 - 11\n                            Class.forName(\n                                \"com.android.server.wm.ActivityStackSupervisor\",\n                                false,\n                                classLoader,\n                            )\n                        } catch (e2: ClassNotFoundException) {\n                            // Android 8.1 - 9\n                            Class.forName(\n                                \"com.android.server.am.ActivityStackSupervisor\",\n                                false,\n                                classLoader,\n                            )\n                        }\n                    }\n\n                // Hook the resolution method to inject our redirection logic\n                LSPosedHelper.hookMethod(\n                    Hooker::class.java,\n                    supervisorClass,\n                    \"resolveActivity\",\n                    Intent::class.java,\n                    ResolveInfo::class.java,\n                    Int::class.javaPrimitiveType,\n                    ProfilerInfo::class.java,\n                )\n\n                Utils.logD(\"Successfully hooked Activity Supervisor for Manager redirection.\")\n            }\n            .onFailure { Utils.logE(\"Failed to hook system server activity resolution\", it) }\n    }\n}\n"
  },
  {
    "path": "zygisk/src/main/kotlin/org/matrix/vector/core/Main.kt",
    "content": "package org.matrix.vector.core\n\nimport android.os.IBinder\nimport android.os.Process\nimport org.lsposed.lspd.core.ApplicationServiceClient.serviceClient\nimport org.lsposed.lspd.core.Startup\nimport org.lsposed.lspd.service.ILSPApplicationService\nimport org.lsposed.lspd.util.Utils\nimport org.matrix.vector.BuildConfig\nimport org.matrix.vector.ParasiticManagerHooker\nimport org.matrix.vector.ParasiticManagerSystemHooker\n\n/** Main entry point for the Java-side loader, invoked via JNI from the Vector Zygisk module. */\nobject Main {\n\n    /**\n     * Shared initialization logic for both System Server and Application processes.\n     *\n     * @param isSystem True if this is the system_server process.\n     * @param isLateInject True if Zygisk APIs are not invoked via hooks\n     * @param niceName The process name (e.g., package name or \"system\").\n     * @param appDir The application's data directory.\n     * @param binder The Binder token associated with the application service.\n     */\n    @JvmStatic\n    fun forkCommon(\n        isSystem: Boolean,\n        isLateInject: Boolean,\n        niceName: String,\n        appDir: String?,\n        binder: IBinder,\n    ) {\n        // Initialize system-specific resolution hooks if in system_server\n        if (isSystem) {\n            ParasiticManagerSystemHooker.start()\n        }\n\n        // Initialize Xposed bridge components\n        val appService = ILSPApplicationService.Stub.asInterface(binder)\n        Startup.initXposed(isSystem, niceName, appDir, appService)\n\n        // Configure logging levels from the service client\n        runCatching { Utils.Log.muted = serviceClient.isLogMuted }\n            .onFailure { t -> Utils.logE(\"Failed to configure logs from service\", t) }\n\n        // Check if this process is the designated Vector Manager.\n        // If so, we perform \"parasitic\" injection into a host (com.android.shell)\n        // and terminate further standard Xposed loading for this specific process.\n        if (niceName == BuildConfig.ManagerPackageName && ParasiticManagerHooker.start()) {\n            Utils.logI(\"Parasitic manager loaded into host, skipping standard bootstrap.\")\n            return\n        }\n\n        // Standard Xposed module loading for third-party apps\n        Utils.logI(\"Loading Vector/Xposed for $niceName (UID: ${Process.myUid()})\")\n        Startup.bootstrapXposed(isSystem && isLateInject)\n    }\n}\n"
  },
  {
    "path": "zygisk/src/main/kotlin/org/matrix/vector/service/BridgeService.kt",
    "content": "package org.matrix.vector.service\n\nimport android.app.ActivityThread\nimport android.os.Binder\nimport android.os.IBinder\nimport android.os.IBinder.DeathRecipient\nimport android.os.Parcel\nimport android.os.Process\nimport hidden.HiddenApiBridge.Binder_allowBlocking\nimport hidden.HiddenApiBridge.Context_getActivityToken\nimport org.lsposed.lspd.service.ILSPosedService\nimport org.lsposed.lspd.util.Utils.Log\n\n/**\n * Manages manual Binder transactions for the Vector framework.\n *\n * This service is not registered in ServiceManager. Instead, the Zygisk native module intercepts\n * [Binder.execTransact] and redirects calls with the [TRANSACTION_CODE] to this class.\n */\nobject BridgeService {\n    private const val TRANSACTION_CODE =\n        ('_'.code shl 24) or ('V'.code shl 16) or ('E'.code shl 8) or 'C'.code\n    private const val TAG = \"VectorBridge\"\n\n    /** Actions supported by the manual IPC bridge. */\n    private enum class Action {\n        UNKNOWN,\n        SEND_BINDER, // Daemon sending the system service binder\n        GET_BINDER, // Process requesting its specific application service\n        ENABLE_MANAGER, // Toggle manager state\n    }\n\n    @Volatile private var serviceBinder: IBinder? = null\n\n    @Volatile private var service: ILSPosedService? = null\n\n    /** Cleans up service references if the remote LSPosed process crashes. */\n    private val serviceRecipient: DeathRecipient = DeathRecipient {\n        Log.e(TAG, \"LSPosed system service died.\")\n        serviceBinder?.unlinkToDeath(this.serviceRecipient, 0)\n        serviceBinder = null\n        service = null\n    }\n\n    /** Returns the active LSPosed system service interface. */\n    @JvmStatic fun getService(): ILSPosedService? = service\n\n    /**\n     * Initializes the client-side connection to the LSPosed system service.\n     *\n     * @param binder The raw binder for [ILSPosedService].\n     */\n    private fun receiveFromBridge(binder: IBinder?) {\n        if (binder == null) {\n            Log.e(TAG, \"Received null binder from bridge.\")\n            return\n        }\n\n        // Cleanup old death recipient if we are re-initializing\n        val token = Binder.clearCallingIdentity()\n        try {\n            serviceBinder?.unlinkToDeath(serviceRecipient, 0)\n        } finally {\n            Binder.restoreCallingIdentity(token)\n        }\n\n        // Allow blocking calls since we are often in a synchronous fork path\n        val blockingBinder = Binder_allowBlocking(binder)\n        serviceBinder = blockingBinder\n        service = ILSPosedService.Stub.asInterface(blockingBinder)\n\n        runCatching { blockingBinder.linkToDeath(serviceRecipient, 0) }\n            .onFailure { Log.e(TAG, \"Failed to link to service death\", it) }\n\n        // Provide the system context to the service so it can manage system-wide states\n        runCatching {\n                val activityThread = ActivityThread.currentActivityThread()\n                val at = activityThread.applicationThread as android.app.IApplicationThread\n                val atBinder = at.asBinder()\n                val systemCtx = activityThread.systemContext\n                service?.dispatchSystemServerContext(\n                    atBinder,\n                    Context_getActivityToken(systemCtx),\n                    \"Zygisk\",\n                )\n            }\n            .onFailure { Log.e(TAG, \"Failed to dispatch system context\", it) }\n\n        Log.i(TAG, \"LSPosed system service binder linked.\")\n    }\n\n    /** Handles manual parcel transactions. Called via reflection/JNI from the native hook. */\n    @JvmStatic\n    fun onTransact(data: Parcel, reply: Parcel?, flags: Int): Boolean {\n        return try {\n            val actionIdx = data.readInt()\n            val action = Action.values().getOrElse(actionIdx) { Action.UNKNOWN }\n\n            Log.d(TAG, \"onTransact: action=$action, callerUid=${Binder.getCallingUid()}\")\n\n            when (action) {\n                Action.SEND_BINDER -> {\n                    // Only allow root (UID 0) to push the initial service binder\n                    if (Binder.getCallingUid() == 0) {\n                        receiveFromBridge(data.readStrongBinder())\n                        reply?.writeNoException()\n                        true\n                    } else false\n                }\n\n                Action.GET_BINDER -> {\n                    val processName = data.readString()\n                    val heartBeat = data.readStrongBinder()\n                    val appService =\n                        service?.requestApplicationService(\n                            Binder.getCallingUid(),\n                            Binder.getCallingPid(),\n                            processName,\n                            heartBeat,\n                        )\n\n                    if (appService != null && reply != null) {\n                        reply.writeNoException()\n                        reply.writeStrongBinder(appService.asBinder())\n                        true\n                    } else false\n                }\n\n                Action.ENABLE_MANAGER -> {\n                    val uid = Binder.getCallingUid()\n                    // Restricted to Root, System, or Shell\n                    if (\n                        (uid == 0 || uid == Process.SHELL_UID || uid == Process.SYSTEM_UID) &&\n                            service != null\n                    ) {\n                        val enabled = data.readInt() == 1\n                        val result = service?.setManagerEnabled(enabled) ?: false\n                        reply?.writeInt(if (result) 1 else 0)\n                        true\n                    } else false\n                }\n\n                else -> false\n            }\n        } catch (e: Throwable) {\n            Log.e(TAG, \"Error handling bridge transaction\", e)\n            false\n        }\n    }\n\n    /**\n     * Entry point for the JNI hook in [IPCBridge.cpp].\n     *\n     * @param obj The Binder object being called.\n     * @param code The transaction code.\n     * @param dataObj Native pointer to the data Parcel.\n     * @param replyObj Native pointer to the reply Parcel.\n     * @param flags Transaction flags.\n     * @return True if the transaction was handled.\n     */\n    @JvmStatic\n    fun execTransact(obj: IBinder, code: Int, dataObj: Long, replyObj: Long, flags: Int): Boolean {\n        if (code != TRANSACTION_CODE) return false\n\n        val data = dataObj.asParcel()\n        val reply = replyObj.asParcel()\n\n        if (data == null || reply == null) {\n            Log.w(TAG, \"Transaction dropped: null parcel pointers.\")\n            return false\n        }\n\n        return try {\n            onTransact(data, reply, flags)\n        } catch (e: Exception) {\n            if (flags and IBinder.FLAG_ONEWAY == 0) {\n                reply.setDataPosition(0)\n                reply.writeException(e)\n            }\n            Log.e(TAG, \"Exception during execTransact\", e)\n            true // We handled it, even if by returning an exception\n        } finally {\n            data.recycle()\n            reply.recycle()\n        }\n    }\n}\n"
  },
  {
    "path": "zygisk/src/main/kotlin/org/matrix/vector/service/ParcelUtils.kt",
    "content": "package org.matrix.vector.service\n\nimport android.os.Parcel\nimport java.lang.reflect.Method\n\n/**\n * Internal utilities for raw [Parcel] manipulation. Used primarily for IPC transactions that bypass\n * standard AIDL.\n */\nobject ParcelUtils {\n\n    private val obtainMethod: Method by lazy {\n        Parcel::class.java.getDeclaredMethod(\"obtain\", Long::class.java).apply {\n            isAccessible = true\n        }\n    }\n\n    /**\n     * Reconstructs a Java [Parcel] object from a native C++ parcel pointer. Required for manual\n     * Binder transaction interception in [BridgeService].\n     *\n     * @param ptr The native pointer address (long).\n     * @return A Java Parcel instance wrapping the native pointer, or null if pointer is 0.\n     */\n    @JvmStatic\n    fun fromNativePointer(ptr: Long): Parcel? {\n        if (ptr == 0L) return null\n        return try {\n            obtainMethod.invoke(null, ptr) as? Parcel\n        } catch (e: Throwable) {\n            throw RuntimeException(\"Failed to obtain Parcel from native pointer: $ptr\", e)\n        }\n    }\n}\n\n/** Extension to allow [Long] native pointers to be treated as Parcels. */\nfun Long.asParcel(): Parcel? = ParcelUtils.fromNativePointer(this)\n"
  },
  {
    "path": "zygisk/zygisk.json",
    "content": "{ }\n"
  }
]