├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── feature_request.yaml │ └── question.yaml ├── dependabot.yml └── workflows │ └── build.yaml ├── .gitignore ├── .idea ├── .gitignore ├── AndroidProjectSystem.xml ├── appInsightsSettings.xml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── deploymentTargetSelector.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml ├── qaplug_profiles.xml ├── runConfigurations.xml └── vcs.xml ├── CONTRIBUTING.md ├── CONTRIBUTING_pt-BR.md ├── CONTRIBUTING_zh-CN.md ├── LICENSE ├── README.md ├── README_en.md ├── README_pt-BR.md ├── app ├── .gitignore ├── build.gradle.kts ├── key.jks ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── xposed_init │ ├── kotlin │ └── top │ │ └── ltfan │ │ └── notdeveloper │ │ ├── Item.kt │ │ ├── ui │ │ ├── activity │ │ │ └── MainActivity.kt │ │ ├── composable │ │ │ ├── PreferenceItem.kt │ │ │ ├── SharedPreference.kt │ │ │ └── StatusCard.kt │ │ └── theme │ │ │ └── Theme.kt │ │ ├── util │ │ └── SystemUtil.kt │ │ └── xposed │ │ ├── Hook.kt │ │ ├── Log.kt │ │ └── ModuleStatus.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── values-night │ └── themes.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values │ ├── strings.xml │ └── themes.xml │ └── xml │ ├── backup_rules.xml │ └── data_extraction_rules.xml ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a report to help us improve 3 | title: '[Bug] ' 4 | labels: ['bug'] 5 | assignees: 6 | - xfqwdsj 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | *(For Chinese users)* **Issue标题请使用英文,内容可用中文** 12 | - type: checkboxes 13 | id: searched_existing_issues 14 | attributes: 15 | label: Existing Issues Search Confirmation 16 | description: Please confirm you have searched for existing issues before submitting a new one. 17 | options: 18 | - label: I have searched for existing issues and did not find a similar one. 19 | required: true 20 | - type: textarea 21 | id: bug_description 22 | attributes: 23 | label: Bug Description 24 | description: Clearly describe the bug. 25 | placeholder: e.g., App crashes on launch 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: steps_to_reproduce 30 | attributes: 31 | label: Steps to Reproduce 32 | description: Provide step-by-step instructions to reproduce the issue. 33 | placeholder: e.g., 1. Open the app 2. Tap on 'Hide USB debugging' 3. Observe crash 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: expected_behavior 38 | attributes: 39 | label: Expected Behavior 40 | description: Describe what you expected to happen. 41 | placeholder: e.g., The switch state changed without crashing 42 | validations: 43 | required: true 44 | - type: markdown 45 | attributes: 46 | value: | 47 | **How to provide screenshots correctly?** 48 | 49 | Since a screenshot may be too long to read, please use the following format to provide screenshots: 50 | 51 | ```markdown 52 |
Screenshots 53 | 54 |
55 | ``` 56 | - type: textarea 57 | id: screenshots 58 | attributes: 59 | label: Screenshots 60 | description: Attach screenshots to help illustrate the issue. 61 | placeholder: Follow the format above to provide screenshots 62 | - type: textarea 63 | id: device_info 64 | attributes: 65 | label: Device Details 66 | description: Provide device and OS details. 67 | placeholder: | 68 | e.g., Device: Xiaomi 14, OS: Xiaomi Hyper OS 1.0.23.12.4.DEV; Android 14 69 | validations: 70 | required: true 71 | - type: markdown 72 | attributes: 73 | value: | 74 | **How to provide logs correctly?** 75 | 76 | Since logs can be lengthy, please refer to the following guidance to provide logs: 77 | 78 | - **Option 1**: Paste the log file directly in the text area below. 79 | - **Option 2**: Use the following format to provide log text: 80 | 81 | ````markdown 82 |
Logs 83 | 84 | ``` 85 | 86 | ``` 87 |
88 | ```` 89 | 90 | This helps us diagnose the issue more effectively. 91 | - type: textarea 92 | id: logs 93 | attributes: 94 | label: Logs 95 | description: Provide logs to help diagnose the issue. 96 | placeholder: Refer to the guidance above to provide logs 97 | validations: 98 | required: true 99 | - type: textarea 100 | id: additional_context 101 | attributes: 102 | label: Additional Context 103 | description: Add any other relevant information. 104 | placeholder: e.g., Related issues 105 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | title: '[Feature] ' 4 | labels: ['enhancement'] 5 | assignees: 6 | - xfqwdsj 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | *(For Chinese users)* **Issue标题请使用英文,内容可用中文** 12 | - type: checkboxes 13 | id: searched_existing_issues 14 | attributes: 15 | label: Existing Issues Search Confirmation 16 | description: Please confirm you have searched for existing issues before submitting a new one. 17 | options: 18 | - label: I have searched for existing issues and did not find a similar one. 19 | required: true 20 | - type: textarea 21 | id: problem_description 22 | attributes: 23 | label: Problem Description 24 | description: Clearly describe the problem you want to solve. 25 | placeholder: e.g., Difficulty navigating settings 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: solution_description 30 | attributes: 31 | label: Proposed Solution 32 | description: Describe your proposed solution in detail. 33 | placeholder: e.g., Add a search bar in settings 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: expected_behavior 38 | attributes: 39 | label: Expected Behavior 40 | description: Describe what you expect to happen after the feature is implemented. 41 | placeholder: e.g., Users can quickly find settings using the search bar 42 | validations: 43 | required: true 44 | - type: textarea 45 | id: alternatives 46 | attributes: 47 | label: Alternatives Considered 48 | description: Describe any alternative solutions or features you've considered. 49 | placeholder: e.g., Group settings into categories 50 | - type: markdown 51 | attributes: 52 | value: | 53 | **How to provide images correctly?** 54 | 55 | Since an image may be too long to read, please use the following format to provide images: 56 | 57 | ```markdown 58 |
Images 59 | 60 |
61 | ``` 62 | - type: textarea 63 | id: images 64 | attributes: 65 | label: Images 66 | description: Attach images or screenshots to support your feature request. 67 | placeholder: Follow the format above to provide images 68 | - type: textarea 69 | id: additional_context 70 | attributes: 71 | label: Additional Context 72 | description: Add any other relevant information (e.g., logs, related issues, references). 73 | placeholder: e.g., Related issues, logs, references 74 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yaml: -------------------------------------------------------------------------------- 1 | name: Question? 2 | description: Please ask a question using Discussions 3 | title: Question 4 | labels: ['question'] 5 | assignees: 6 | - xfqwdsj 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | *For questions, please use [Discussions](https://github.com/xfqwdsj/IAmNotADeveloper/discussions) and don't submit an issue.* 12 | - type: checkboxes 13 | id: read 14 | attributes: 15 | label: Read the text above 16 | description: Please confirm you have read the text above. 17 | options: 18 | - label: I have read the text above and will not submit an issue for a question. 19 | required: true 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Building Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | - name: Set up JDK 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: temurin 22 | java-version: '19' 23 | - name: Set up cache 24 | uses: actions/cache@v4 25 | with: 26 | path: | 27 | ~/.gradle/caches 28 | ~/.gradle/wrapper 29 | key: ${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }} 30 | - name: Build 31 | run: | 32 | chmod +x ./gradlew 33 | ./gradlew assembleRelease 34 | - name: Upload artifact 35 | uses: actions/upload-artifact@v4 36 | with: 37 | name: NotDeveloper-${{ github.sha }} 38 | path: app/build/outputs/apk/release/app-release.apk 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /.kotlin/ 17 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/appInsightsSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 37 | 38 | 39 | 41 | 42 | 154 | 155 | 157 | 158 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/qaplug_profiles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 933 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | **English** | [简体中文](CONTRIBUTING_zh-CN.md) | [Português (Brasil)](CONTRIBUTING_pt-BR.md) 2 | 3 | # Contributing Guide 4 | 5 | Thank you for your interest in contributing to this project! Please follow the steps below to get started: 6 | 7 | ## How to Contribute 8 | 9 | 1. **Fork this repository** 10 | 11 | Click the Fork button at the top right corner to fork this project to your GitHub account. 12 | 13 | 2. **Create a branch** 14 | 15 | Clone your fork locally and create a new branch based on `main` for your development. Suggested branch names: `feature/xxx`, `fix/xxx`, etc. 16 | 17 | 3. **Make changes** 18 | 19 | Keep your commit history clear and concise. Please write meaningful commit messages for each change. 20 | 21 | 4. **Push your branch and open a Pull request** 22 | 23 | Push your branch to GitHub and open a [Pull request (PR)](https://github.com/xfqwdsj/IAmNotADeveloper/pulls) in this repository. Please describe your changes and the motivation in detail in the PR description. 24 | 25 | 5. **Code review** 26 | 27 | Maintainers will review your PR and may suggest changes. Please respond promptly and make adjustments as needed. 28 | 29 | 6. **Merge and release** 30 | 31 | After approval, maintainers will merge your changes. Your contribution will appear in the next release. 32 | 33 | ## Contribution Tips 34 | 35 | - Follow the project's code style and conventions. 36 | - Ensure local build and tests pass before submitting. 37 | - Update relevant documentation if your changes affect docs. 38 | - For large or breaking changes, discuss first in [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues). 39 | 40 | ## Reporting Issues 41 | 42 | To report bugs or suggest new features, please submit them in [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) with as much detail as possible (logs, screenshots, etc.). 43 | 44 | ## Contact 45 | 46 | If you have questions, contact us via [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) or [Discussions](https://github.com/xfqwdsj/IAmNotADeveloper/discussions). 47 | -------------------------------------------------------------------------------- /CONTRIBUTING_pt-BR.md: -------------------------------------------------------------------------------- 1 | [English](CONTRIBUTING.md) | [简体中文](CONTRIBUTING_zh-CN.md) | **Português (Brasil)** 2 | 3 | # Guia de contribuição 4 | 5 | Agradecemos seu interesse em contribuir com este projeto! Siga os passos abaixo para começar: 6 | 7 | ## Como contribuir 8 | 9 | 1. **Faça um fork deste repositório** 10 | 11 | Clique no botão Fork no canto superior direito para bifurcar este projeto para sua conta do GitHub. 12 | 13 | 2. **Crie uma branch** 14 | 15 | Clone seu fork localmente e crie uma nova branch baseada em `main` para o seu desenvolvimento. Nomes de branch sugeridos: `feature/xxx`, `fix/xxx`, etc. 16 | 17 | 3. **Faça as alterações** 18 | 19 | Mantenha seu histórico de commits claro e conciso. Escreva mensagens de commit significativas para cada alteração. 20 | 21 | 4. **Envie sua branch e abra um Pull request** 22 | 23 | Envie sua branch para o GitHub e abra um [Pull request (PR)](https://github.com/xfqwdsj/IAmNotADeveloper/pulls) neste repositório. Descreva suas alterações e a motivação em detalhes na descrição do PR. 24 | 25 | 5. **Revisão de código** 26 | 27 | Os mantenedores analisarão seu PR e poderão sugerir alterações. Responda prontamente e faça os ajustes necessários. 28 | 29 | 6. **Mesclar e lançar** 30 | 31 | Após a aprovação, os mantenedores mesclarão suas alterações. Sua contribuição aparecerá na próxima versão. 32 | 33 | ## Dicas de contribuição 34 | 35 | - Siga o estilo e as convenções do código do projeto. 36 | - Garanta que a compilação e os testes locais sejam aprovados antes do envio. 37 | - Atualize a documentação relevante se suas alterações afetarem os documentos. 38 | - Para mudanças grandes ou significativas, discuta primeiro em [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues). 39 | 40 | ## Relatando problemas 41 | 42 | Para relatar bugs ou sugerir novos recursos, envie-os em [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) com o máximo de detalhes possível (logs, capturas de tela, etc.). 43 | 44 | ## Contato 45 | 46 | Se você tiver dúvidas, entre em contato via [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) ou [Discussions](https://github.com/xfqwdsj/IAmNotADeveloper/discussions). 47 | -------------------------------------------------------------------------------- /CONTRIBUTING_zh-CN.md: -------------------------------------------------------------------------------- 1 | [English](CONTRIBUTING.md) | **简体中文** | [Português (Brasil)](CONTRIBUTING_pt-BR.md) 2 | 3 | # 贡献指南 4 | 5 | 感谢你有意为本项目做出贡献!请按照以下步骤参与贡献: 6 | 7 | ## 如何贡献 8 | 9 | 1. **Fork本仓库** 10 | 11 | 点击右上角的Fork按钮,将本项目Fork到你的GitHub账户下。 12 | 13 | 2. **创建分支** 14 | 15 | 在本地克隆你的Fork,并基于`main`分支创建新分支进行开发。分支命名建议:`feature/xxx`、`fix/xxx`等。 16 | 17 | 3. **提交更改** 18 | 19 | 保持提交记录清晰、简洁。每次提交请编写有意义的提交信息。 20 | 21 | 4. **推送分支并发起Pull request** 22 | 23 | 将你的分支推送到GitHub,并在本仓库发起[Pull request(简称PR)](https://github.com/xfqwdsj/IAmNotADeveloper/pulls)。请在PR描述中详细说明你的更改内容和动机。 24 | 25 | 5. **代码评审** 26 | 27 | 维护者会对你的PR进行评审,可能会提出修改建议。请及时响应并根据建议进行调整。 28 | 29 | 6. **合并与发布** 30 | 31 | 通过评审后,维护者会合并你的更改。你的贡献将出现在下一个版本中。 32 | 33 | ## 贡献建议 34 | 35 | - 遵循项目的代码风格和规范。 36 | - 提交前请确保本地构建和测试通过。 37 | - 如涉及文档变更,请同步更新相关文档。 38 | - 对于较大或破坏性更改,建议先在[Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues)区讨论。 39 | 40 | ## 问题反馈 41 | 42 | 如需报告Bug或建议新功能,请在[Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues)区提交,并尽量提供详细信息(如日志、截图等)。 43 | 44 | ## 联系方式 45 | 46 | 如有疑问,可通过[Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues)或[Discussions](https://github.com/xfqwdsj/IAmNotADeveloper/discussions)与我们联系。 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 LTFan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/xfqwdsj/IAmNotADeveloper) 2 | 3 | **简体中文** | [English](README_en.md) | [Português (Brasil)](README_pt-BR.md) 4 | 5 | # 我不是开发者 6 | 7 | 一个用于隐藏Android系统开发者相关选项状态的模块。 8 | 9 | ## FAQ 10 | 11 | ### Q: 我激活了模块,但模块报告“未激活”,这是怎么回事? 12 | 13 | 排查步骤: 14 | 15 | 1. 确认您已经激活了模块。 16 | 2. 确认您在成功激活模块后,强行停止了模块应用。 17 | 3. 在[Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues)中搜索相关问题。 18 | 4. 如果没有找到相关问题,请先抓取日志并对模块应用截图,确保包含完整的模块状态卡片(如果无法在一张截图中包含,请分多张截图)。 19 | 5. 在[Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues)中提交新问题,并上传日志。 20 | 21 | ### Q: 如何确认LSPosed中,模块已经激活? 22 | 23 | 您可以通过以下步骤确认: 24 | 25 | 1. 通过任意方式启动LSPosed管理器。 26 | 2. 在“模块”页面中,找到“我不是开发者”模块。 27 | 3. 确认“启用模块”开关处于开启状态。 28 | 29 | ### Q: 如何抓取日志? 30 | 31 | 您可以通过以下步骤抓取日志: 32 | 33 | 1. 通过任意方式启动LSPosed管理器。 34 | 2. 在“日志”页面中,点击右上角的“保存”图标按钮。 35 | 3. 选择一个合适的保存位置,如“下载内容”,不要修改文件名。 36 | 4. 点击“保存”按钮。 37 | 38 | ### Q: 我对某应用程序激活了模块,但是应用程序崩溃了/没有任何效果/被检测器应用检测到了,怎么办? 39 | 40 | 本模块的实现方式为直接注入目标应用程序,对于内置注入防护的应用程序,模块可能会不起作用,甚至会使应用拒绝工作。 41 | 42 | 解决方案:无。[#104](https://github.com/xfqwdsj/IAmNotADeveloper/issues/104)已经立项,您可以耐心等待,没有预计完成时间。**请不要提交关于此问题的任何Issue,它将会被直接关闭。** 43 | 44 | ## 如何贡献 45 | 46 | 如果您想为本项目贡献代码,请参考[CONTRIBUTING.md](CONTRIBUTING_zh-CN.md)。 47 | 48 | ## 隐私协议 49 | 50 | 本应用的“测试”功能会获取您对应系统开关的状态,包括: 51 | 52 | - 开发者模式 53 | - USB 调试 54 | - 无线调试 55 | 56 | 但是本应用不会收集您的任何信息。 57 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/xfqwdsj/IAmNotADeveloper) 2 | 3 | [简体中文](README.md) | **English** | [Português (Brasil)](README_pt-BR.md) 4 | 5 | # IAmNotADeveloper 6 | 7 | A module for hiding the status of Android system developer options. 8 | 9 | ## FAQ 10 | 11 | ### Q: I have activated the module, but it reports "Module not activated". What should I do? 12 | 13 | Troubleshooting steps: 14 | 15 | 1. Make sure you have activated the module. 16 | 2. Make sure you have force stopped the module app after successful activation. 17 | 3. Search for related issues in the [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) section. 18 | 4. If you cannot find a related issue, please capture logs and take screenshots of the module app, ensuring the full module status card is visible (if it cannot fit in one screenshot, use multiple). 19 | 5. Submit a new issue in [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) and upload the logs. 20 | 21 | ### Q: How do I confirm that the module is activated in LSPosed? 22 | 23 | You can confirm by following these steps: 24 | 25 | 1. Launch the LSPosed Manager by any means. 26 | 2. On the "Modules" page, find the "IAmNotADeveloper" module. 27 | 3. Make sure the "Enable module" switch is turned on. 28 | 29 | ### Q: How do I capture logs? 30 | 31 | You can capture logs by following these steps: 32 | 33 | 1. Launch the LSPosed Manager by any means. 34 | 2. On the "Logs" page, tap the "Save" icon button in the upper right corner. 35 | 3. Choose a suitable save location, such as "Downloads", and do not change the file name. 36 | 4. Tap the "Save" button. 37 | 38 | ### Q: I activated the module for a certain app, but the app crashes/has no effect/is detected by a detector app. What should I do? 39 | 40 | This module works by directly injecting into the target app. For apps with built-in injection protection, the module may not work or may cause the app to refuse to run. 41 | 42 | Solution: None at the moment. See [Issue #104](https://github.com/xfqwdsj/IAmNotADeveloper/issues/104) for details. Please wait patiently; there is currently no ETA (Estimated Time of Arrival). **Do not submit any issues regarding this problem; they will be closed without further explanation.** 43 | 44 | ## How to Contribute 45 | 46 | If you want to contribute code to this project, please refer to [CONTRIBUTING.md](CONTRIBUTING.md). 47 | 48 | ## Privacy Agreement 49 | 50 | The "Test" function of this application will obtain the status of your corresponding system switch, including: 51 | 52 | - Developer mode 53 | - USB debugging 54 | - Wireless debugging 55 | 56 | However, this application will not collect any information about you. 57 | -------------------------------------------------------------------------------- /README_pt-BR.md: -------------------------------------------------------------------------------- 1 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/xfqwdsj/IAmNotADeveloper) 2 | 3 | [简体中文](README.md) | [English](README_en.md) | **Português (Brasil)** 4 | 5 | # IAmNotADeveloper 6 | 7 | Um módulo para ocultar o status das opções do desenvolvedor do sistema Android. 8 | 9 | ## Perguntas frequentes 10 | 11 | ### P: Ativei o módulo, mas ele informa "Módulo não ativado". O que devo fazer? 12 | 13 | Etapas de solução de problemas: 14 | 15 | 1. Certifique-se de ter ativado o módulo. 16 | 2. Certifique-se de que você tenha forçado a parada do app do módulo após a ativação bem-sucedida. 17 | 3. Pesquise por problemas relacionados na seção [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues). 18 | 4. Se você não conseguir encontrar um problema relacionado, capture logs e faça capturas de tela do app do módulo, garantindo que o cartão de status completo do módulo esteja visível (se não couber em uma captura de tela, use várias). 19 | 5. Envie um novo problema em [Issues](https://github.com/xfqwdsj/IAmNotADeveloper/issues) e carregue os logs. 20 | 21 | ### P: Como posso confirmar se o módulo está ativado no LSPosed? 22 | 23 | Você pode confirmar seguindo estes passos: 24 | 25 | 1. Abra o LSPosed por qualquer meio. 26 | 2. Na página "Módulos", localize o módulo "IAmNotADeveloper". 27 | 3. Certifique-se de que a chave "Ativar módulo" esteja ativada. 28 | 29 | ### P: Como faço para capturar logs? 30 | 31 | Você pode capturar logs seguindo estes passos: 32 | 33 | 1. Abra o LSPosed por qualquer meio. 34 | 2. Na página "Registros", toque no ícone "Salvar" no canto superior direito. 35 | 3. Escolha um local adequado para salvar, como "Downloads", e não altere o nome do arquivo. 36 | 4. Toque no botão "Salvar". 37 | 38 | ### P: Ativei o módulo para um determinado app, mas o app trava/não tem efeito/é detectado por um app detector. O que devo fazer? 39 | 40 | Este módulo funciona injetando diretamente no app de destino. Para apps com proteção contra injeção integrada, o módulo pode não funcionar ou fazer com que o app se recuse a executar. 41 | 42 | Solução: Nenhuma no momento. Consulte o [Issue #104](https://github.com/xfqwdsj/IAmNotADeveloper/issues/104) para obter detalhes. Aguarde pacientemente; no momento, não há ETA (Hora Estimada de Chegada). **Não envie nenhum issue relacionado a este problema; eles serão encerrados sem maiores explicações.** 43 | 44 | ## Como contribuir 45 | 46 | Se você quiser contribuir com código para este projeto, consulte [CONTRIBUTING.md](CONTRIBUTING_pt-BR.md). 47 | 48 | ## Acordo de privacidade 49 | 50 | A função "Testar" deste app obterá o status das chaves do sistema correspondentes, incluindo: 51 | 52 | - Opções do desenvolvedor 53 | - Depuração USB 54 | - Depuração por Wi-Fi 55 | 56 | No entanto, este app não coletará nenhuma informação sobre você. 57 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | alias(libs.plugins.kotlin) 5 | alias(libs.plugins.androidApplication) 6 | alias(libs.plugins.composeCompiler) 7 | } 8 | 9 | kotlin { 10 | compilerOptions { 11 | jvmTarget.set(JvmTarget.JVM_11) 12 | } 13 | } 14 | 15 | android { 16 | val appId = "top.ltfan.notdeveloper" 17 | 18 | namespace = appId 19 | compileSdk = libs.versions.compileSdk.get().toInt() 20 | 21 | signingConfigs { 22 | create("config") { 23 | storeFile = file("key.jks") 24 | storePassword = "keykey" 25 | keyAlias = "keykey" 26 | keyPassword = "keykey" 27 | } 28 | } 29 | 30 | defaultConfig { 31 | applicationId = appId 32 | minSdk = libs.versions.minSdk.get().toInt() 33 | targetSdk = libs.versions.targetSdk.get().toInt() 34 | versionName = libs.versions.app.versionName.get() 35 | versionCode = libs.versions.app.versionCode.get().toInt() 36 | } 37 | 38 | buildTypes { 39 | release { 40 | isMinifyEnabled = true 41 | isShrinkResources = true 42 | proguardFiles( 43 | getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 44 | ) 45 | signingConfig = signingConfigs.getByName("config") 46 | } 47 | 48 | debug { 49 | signingConfig = signingConfigs.getByName("config") 50 | } 51 | } 52 | 53 | buildFeatures { 54 | buildConfig = true 55 | } 56 | 57 | compileOptions { 58 | sourceCompatibility = JavaVersion.VERSION_11 59 | targetCompatibility = JavaVersion.VERSION_11 60 | } 61 | 62 | buildFeatures { 63 | compose = true 64 | } 65 | 66 | packaging { 67 | resources { 68 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 69 | } 70 | } 71 | } 72 | 73 | dependencies { 74 | implementation(libs.kotlin.reflect) 75 | implementation(libs.lifecycle.runtime) 76 | implementation(libs.lifecycle.viewmodel) 77 | implementation(libs.activity.compose) 78 | 79 | val compose = platform(libs.compose) 80 | implementation(compose) 81 | 82 | implementation(libs.compose.runtime) 83 | implementation(libs.compose.foundation) 84 | implementation(libs.compose.ui) 85 | implementation(libs.compose.ui.tooling.preview) 86 | implementation(libs.compose.animation) 87 | implementation(libs.compose.material3) 88 | implementation(libs.preference) 89 | compileOnly(libs.xposed.api) 90 | } 91 | -------------------------------------------------------------------------------- /app/key.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/key.jks -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | top.ltfan.notdeveloper.xposed.Hook -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/Item.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper 2 | 3 | import androidx.annotation.StringRes 4 | 5 | enum class Item(val key: String, @StringRes val nameId: Int) { 6 | DevelopmentSettingsEnabled("development_settings_enabled", R.string.toggle_hide_development_mode), 7 | AdbEnabled("adb_enabled", R.string.toggle_hide_usb_debugging), 8 | AdbWifiEnabled("adb_wifi_enabled", R.string.toggle_hide_wireless_debugging); 9 | 10 | companion object { 11 | val oldApiItems = listOf(DevelopmentSettingsEnabled, AdbEnabled) 12 | val newApiItems = listOf(AdbWifiEnabled) 13 | val settingGlobalItems = listOf(DevelopmentSettingsEnabled, AdbEnabled, AdbWifiEnabled) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/ui/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.ui.activity 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import android.provider.Settings 6 | import android.view.WindowManager 7 | import androidx.activity.ComponentActivity 8 | import androidx.activity.compose.setContent 9 | import androidx.activity.enableEdgeToEdge 10 | import androidx.compose.foundation.layout.Column 11 | import androidx.compose.foundation.layout.Spacer 12 | import androidx.compose.foundation.layout.WindowInsets 13 | import androidx.compose.foundation.layout.WindowInsetsSides 14 | import androidx.compose.foundation.layout.consumeWindowInsets 15 | import androidx.compose.foundation.layout.displayCutout 16 | import androidx.compose.foundation.layout.fillMaxSize 17 | import androidx.compose.foundation.layout.height 18 | import androidx.compose.foundation.layout.only 19 | import androidx.compose.foundation.layout.padding 20 | import androidx.compose.foundation.layout.safeDrawing 21 | import androidx.compose.foundation.layout.windowInsetsPadding 22 | import androidx.compose.foundation.rememberScrollState 23 | import androidx.compose.foundation.verticalScroll 24 | import androidx.compose.material3.ExperimentalMaterial3Api 25 | import androidx.compose.material3.LargeTopAppBar 26 | import androidx.compose.material3.Scaffold 27 | import androidx.compose.material3.Text 28 | import androidx.compose.material3.TopAppBarDefaults 29 | import androidx.compose.material3.rememberTopAppBarState 30 | import androidx.compose.runtime.getValue 31 | import androidx.compose.runtime.mutableStateMapOf 32 | import androidx.compose.runtime.mutableStateOf 33 | import androidx.compose.runtime.setValue 34 | import androidx.compose.ui.Alignment 35 | import androidx.compose.ui.Modifier 36 | import androidx.compose.ui.input.nestedscroll.nestedScroll 37 | import androidx.compose.ui.res.stringResource 38 | import androidx.compose.ui.unit.dp 39 | import top.ltfan.notdeveloper.Item 40 | import top.ltfan.notdeveloper.R 41 | import top.ltfan.notdeveloper.ui.composable.PreferenceItem 42 | import top.ltfan.notdeveloper.ui.composable.StatusCard 43 | import top.ltfan.notdeveloper.ui.composable.rememberBooleanSharedPreference 44 | import top.ltfan.notdeveloper.ui.theme.IAmNotADeveloperTheme 45 | import top.ltfan.notdeveloper.util.isMiui 46 | import top.ltfan.notdeveloper.xposed.statusIsPreferencesReady 47 | 48 | class MainActivity : ComponentActivity() { 49 | private var isPreferencesReady by mutableStateOf(false) 50 | private val testResults = mutableStateMapOf() 51 | 52 | @SuppressLint("WorldReadableFiles") 53 | @OptIn(ExperimentalMaterial3Api::class) 54 | override fun onCreate(savedInstanceState: Bundle?) { 55 | enableEdgeToEdge() 56 | @Suppress("DEPRECATION") if (isMiui) { 57 | window.setFlags( 58 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, 59 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 60 | ) 61 | window.setFlags( 62 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 63 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION 64 | ) 65 | } 66 | super.onCreate(savedInstanceState) 67 | 68 | setContent { 69 | IAmNotADeveloperTheme { 70 | val scrollBehavior = 71 | TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) 72 | 73 | Scaffold( 74 | modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), 75 | topBar = { 76 | LargeTopAppBar( 77 | title = { 78 | Text(stringResource(R.string.app_name)) 79 | }, 80 | windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top), 81 | scrollBehavior = scrollBehavior 82 | ) 83 | } 84 | ) { padding -> 85 | Column( 86 | modifier = Modifier 87 | .consumeWindowInsets(padding) 88 | .fillMaxSize() 89 | .verticalScroll(rememberScrollState()) 90 | .padding(padding) 91 | .windowInsetsPadding(WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)), 92 | horizontalAlignment = Alignment.CenterHorizontally 93 | ) { 94 | Spacer(Modifier.height(16.dp)) 95 | 96 | StatusCard( 97 | modifier = Modifier.padding(horizontal = 16.dp), 98 | isPreferencesReady = isPreferencesReady 99 | ) 100 | 101 | Spacer(Modifier.height(16.dp)) 102 | 103 | for (item in Item.entries) { 104 | @Suppress("DEPRECATION") var pref by rememberBooleanSharedPreference( 105 | mode = MODE_WORLD_READABLE, 106 | key = item.key, 107 | defaultValue = true, 108 | afterSet = { check() } 109 | ) 110 | val testResult = testResults[item] ?: false 111 | 112 | PreferenceItem( 113 | nameId = item.nameId, 114 | testResult = testResult, 115 | checked = pref, 116 | onClick = { pref = !pref }, 117 | enabled = isPreferencesReady 118 | ) 119 | } 120 | 121 | Spacer(Modifier.height(16.dp)) 122 | } 123 | } 124 | } 125 | } 126 | } 127 | 128 | override fun onResume() { 129 | super.onResume() 130 | isPreferencesReady = statusIsPreferencesReady 131 | check() 132 | } 133 | 134 | private fun check() { 135 | Item.settingGlobalItems.forEach { 136 | testResults[it] = Settings.Global.getInt( 137 | contentResolver, 138 | it.key, 139 | 0 140 | ) == 1 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/ui/composable/PreferenceItem.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.ui.composable 2 | 3 | import androidx.annotation.StringRes 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.material.icons.Icons 6 | import androidx.compose.material.icons.filled.Check 7 | import androidx.compose.material.icons.filled.Clear 8 | import androidx.compose.material3.Icon 9 | import androidx.compose.material3.ListItem 10 | import androidx.compose.material3.Switch 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.res.stringResource 15 | import top.ltfan.notdeveloper.R 16 | 17 | @Composable 18 | fun PreferenceItem( 19 | @StringRes nameId: Int, 20 | testResult: Boolean, 21 | checked: Boolean, 22 | onClick: () -> Unit, 23 | enabled: Boolean, 24 | ) { 25 | ListItem( 26 | headlineContent = { 27 | Text(stringResource(nameId)) 28 | }, 29 | modifier = Modifier.clickable(enabled = enabled, onClick = onClick), 30 | leadingContent = { 31 | if (testResult) { 32 | Icon( 33 | Icons.Default.Clear, 34 | contentDescription = stringResource(R.string.test_result_on) 35 | ) 36 | } else { 37 | Icon( 38 | Icons.Default.Check, 39 | contentDescription = stringResource(R.string.test_result_off) 40 | ) 41 | } 42 | }, 43 | trailingContent = { 44 | Switch(checked = checked, onCheckedChange = null, enabled = enabled) 45 | } 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/ui/composable/SharedPreference.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.ui.composable 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.DisposableEffect 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.runtime.setValue 11 | import androidx.compose.ui.platform.LocalContext 12 | import androidx.core.content.edit 13 | import kotlinx.coroutines.CoroutineScope 14 | import kotlinx.coroutines.Dispatchers 15 | import kotlinx.coroutines.launch 16 | import kotlin.reflect.KProperty 17 | 18 | @Composable 19 | fun rememberBooleanSharedPreference( 20 | preferenceFileKey: String? = null, 21 | mode: Int = Context.MODE_PRIVATE, 22 | key: String, 23 | defaultValue: Boolean, 24 | beforeSet: ((Boolean) -> Boolean)? = null, 25 | afterSet: ((Boolean) -> Unit)? = null, 26 | ): BooleanSharedPreference { 27 | val context = LocalContext.current 28 | val preference = remember(key) { 29 | BooleanSharedPreference( 30 | context, preferenceFileKey, mode, key, defaultValue, beforeSet, afterSet 31 | ) 32 | } 33 | 34 | DisposableEffect(preference) { 35 | onDispose { 36 | preference.clean() 37 | } 38 | } 39 | 40 | return preference 41 | } 42 | 43 | class BooleanSharedPreference( 44 | context: Context, 45 | preferenceFileKey: String? = null, 46 | mode: Int = Context.MODE_PRIVATE, 47 | private val key: String, 48 | private val defaultValue: Boolean, 49 | private val beforeSet: ((Boolean) -> Boolean)? = null, 50 | private val afterSet: ((Boolean) -> Unit)? = null, 51 | ) { 52 | private val sharedPreferences = runCatching { 53 | context.getSharedPreferences( 54 | preferenceFileKey ?: (context.packageName + "_preferences"), mode 55 | ) 56 | }.getOrNull() 57 | 58 | private val listener = OnSharedPreferenceChangeListener { sharedPreferences, changedKey -> 59 | if (changedKey != key) { 60 | return@OnSharedPreferenceChangeListener 61 | } 62 | 63 | value = sharedPreferences.getBoolean(key, defaultValue) 64 | } 65 | 66 | init { 67 | sharedPreferences?.registerOnSharedPreferenceChangeListener(listener) 68 | } 69 | 70 | private val prefsValue get() = sharedPreferences?.getBoolean(key, defaultValue) ?: defaultValue 71 | 72 | private var value by mutableStateOf(prefsValue) 73 | 74 | operator fun getValue(thisObj: Any?, property: KProperty<*>) = value 75 | 76 | operator fun setValue(thisObj: Any?, property: KProperty<*>, value: Boolean) { 77 | CoroutineScope(Dispatchers.IO).launch { 78 | val prefsValue = beforeSet?.invoke(value) ?: value 79 | sharedPreferences?.edit(commit = true) { putBoolean(key, prefsValue) } 80 | this@BooleanSharedPreference.value = prefsValue 81 | afterSet?.invoke(prefsValue) 82 | } 83 | } 84 | 85 | fun clean() { 86 | sharedPreferences?.unregisterOnSharedPreferenceChangeListener(listener) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/ui/composable/StatusCard.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.ui.composable 2 | 3 | import androidx.compose.animation.AnimatedVisibility 4 | import androidx.compose.animation.expandVertically 5 | import androidx.compose.animation.shrinkVertically 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.Spacer 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.height 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.size 13 | import androidx.compose.foundation.layout.width 14 | import androidx.compose.material.icons.Icons 15 | import androidx.compose.material.icons.filled.CheckCircle 16 | import androidx.compose.material.icons.filled.Warning 17 | import androidx.compose.material3.CardDefaults 18 | import androidx.compose.material3.ElevatedCard 19 | import androidx.compose.material3.Icon 20 | import androidx.compose.material3.MaterialTheme 21 | import androidx.compose.material3.Text 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.runtime.getValue 24 | import androidx.compose.runtime.mutableStateOf 25 | import androidx.compose.runtime.remember 26 | import androidx.compose.runtime.setValue 27 | import androidx.compose.ui.Alignment 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.graphics.Color 30 | import androidx.compose.ui.graphics.vector.ImageVector 31 | import androidx.compose.ui.res.stringResource 32 | import androidx.compose.ui.tooling.preview.Preview 33 | import androidx.compose.ui.unit.dp 34 | import top.ltfan.notdeveloper.R 35 | import top.ltfan.notdeveloper.xposed.statusIsModuleActivated 36 | 37 | @Composable 38 | fun StatusCard( 39 | modifier: Modifier = Modifier, 40 | isModuleActivated: Boolean = statusIsModuleActivated, 41 | isPreferencesReady: Boolean 42 | ) { 43 | val status = Status.from(isModuleActivated, isPreferencesReady) 44 | 45 | var expanded by remember { mutableStateOf(false) } 46 | 47 | ElevatedCard( 48 | onClick = { expanded = !expanded }, 49 | modifier = modifier.fillMaxWidth(), 50 | colors = CardDefaults.cardColors(containerColor = status.containerColor), 51 | ) { 52 | Row( 53 | Modifier 54 | .padding(24.dp) 55 | .fillMaxWidth(), 56 | verticalAlignment = Alignment.CenterVertically 57 | ) { 58 | Icon(status.icon, contentDescription = null, modifier = Modifier.size(32.dp)) 59 | Spacer(Modifier.width(16.dp)) 60 | Column(Modifier.weight(1f)) { 61 | Text(status.summary, style = MaterialTheme.typography.headlineSmall) 62 | AnimatedVisibility(visible = isPreferencesReady) { 63 | Text(stringResource(R.string.description_changes_application)) 64 | } 65 | AnimatedVisibility(visible = status != Status.Normal) { 66 | Text(stringResource(R.string.description_more_info)) 67 | } 68 | 69 | AnimatedVisibility( 70 | visible = status != Status.Normal || expanded, 71 | enter = expandVertically(expandFrom = Alignment.CenterVertically), 72 | exit = shrinkVertically(shrinkTowards = Alignment.CenterVertically) 73 | ) { 74 | Spacer(Modifier.height(8.dp)) 75 | } 76 | AnimatedVisibility(visible = expanded && isModuleActivated) { 77 | Text(stringResource(R.string.status_entry_activation_y)) 78 | } 79 | AnimatedVisibility(visible = !isModuleActivated) { 80 | Text( 81 | stringResource(R.string.status_entry_activation_n), 82 | color = MaterialTheme.colorScheme.error 83 | ) 84 | } 85 | AnimatedVisibility(visible = expanded) { 86 | Text( 87 | stringResource(R.string.status_entry_activation_description), 88 | style = MaterialTheme.typography.labelSmall 89 | ) 90 | } 91 | 92 | AnimatedVisibility( 93 | visible = expanded, 94 | enter = expandVertically(expandFrom = Alignment.CenterVertically), 95 | exit = shrinkVertically(shrinkTowards = Alignment.CenterVertically) 96 | ) { 97 | Spacer(Modifier.height(8.dp)) 98 | } 99 | 100 | AnimatedVisibility(visible = expanded && isPreferencesReady) { 101 | Text(stringResource(R.string.status_entry_prefs_y)) 102 | } 103 | AnimatedVisibility(visible = !isPreferencesReady) { 104 | Text( 105 | stringResource(R.string.status_entry_prefs_n), 106 | color = MaterialTheme.colorScheme.error 107 | ) 108 | } 109 | AnimatedVisibility(visible = expanded) { 110 | Text( 111 | stringResource(R.string.status_entry_prefs_description), 112 | style = MaterialTheme.typography.labelSmall 113 | ) 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | @Preview(device = "id:pixel_5", locale = "zh-rCN", showSystemUi = false, showBackground = false) 121 | @Composable 122 | fun StatusCardPreview() { 123 | Column { 124 | StatusCard(isModuleActivated = true, isPreferencesReady = true) 125 | StatusCard(isModuleActivated = true, isPreferencesReady = false) 126 | StatusCard(isModuleActivated = false, isPreferencesReady = true) 127 | StatusCard(isModuleActivated = false, isPreferencesReady = false) 128 | } 129 | } 130 | 131 | enum class Status { 132 | Normal, Partial, Error; 133 | 134 | companion object { 135 | fun from(isModuleActivated: Boolean, isPreferencesReady: Boolean): Status { 136 | if (!isModuleActivated) return Error 137 | return if (isPreferencesReady) { 138 | Normal 139 | } else Partial 140 | } 141 | } 142 | 143 | val containerColor: Color 144 | @Composable get() = when (this) { 145 | Normal -> MaterialTheme.colorScheme.primaryContainer 146 | Partial -> MaterialTheme.colorScheme.tertiaryContainer 147 | Error -> MaterialTheme.colorScheme.errorContainer 148 | } 149 | 150 | val icon: ImageVector 151 | @Composable get() = when (this) { 152 | Normal -> Icons.Default.CheckCircle 153 | else -> Icons.Default.Warning 154 | } 155 | 156 | val summary: String 157 | @Composable get() = stringResource( 158 | when (this) { 159 | Normal -> R.string.status_normal 160 | Partial -> R.string.status_partial 161 | Error -> R.string.status_error 162 | } 163 | ) 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.ui.theme 2 | 3 | import android.os.Build 4 | import androidx.compose.foundation.isSystemInDarkTheme 5 | import androidx.compose.material3.MaterialTheme 6 | import androidx.compose.material3.darkColorScheme 7 | import androidx.compose.material3.dynamicDarkColorScheme 8 | import androidx.compose.material3.dynamicLightColorScheme 9 | import androidx.compose.material3.lightColorScheme 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.platform.LocalContext 12 | 13 | @Composable 14 | fun IAmNotADeveloperTheme( 15 | darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit 16 | ) { 17 | val colorScheme = when { 18 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 19 | val context = LocalContext.current 20 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 21 | } 22 | 23 | darkTheme -> darkColorScheme() 24 | else -> lightColorScheme() 25 | } 26 | 27 | MaterialTheme( 28 | colorScheme = colorScheme, content = content 29 | ) 30 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/util/SystemUtil.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.util 2 | 3 | import android.annotation.SuppressLint 4 | import kotlin.reflect.full.declaredFunctions 5 | 6 | val isMiui: Boolean 7 | @SuppressLint("PrivateApi") get() { 8 | val clazz = Class.forName("android.os.SystemProperties").kotlin 9 | val method = 10 | clazz.declaredFunctions.firstOrNull { it.name == "get" && it.parameters.size == 1 } 11 | return method?.call("ro.miui.ui.version.name") != "" 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/xposed/Hook.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.xposed 2 | 3 | import android.content.ContentResolver 4 | import android.provider.Settings 5 | import androidx.annotation.Keep 6 | import de.robv.android.xposed.IXposedHookLoadPackage 7 | import de.robv.android.xposed.XC_MethodHook 8 | import de.robv.android.xposed.XC_MethodHook.MethodHookParam 9 | import de.robv.android.xposed.XSharedPreferences 10 | import de.robv.android.xposed.XposedBridge 11 | import de.robv.android.xposed.XposedHelpers 12 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam 13 | import top.ltfan.notdeveloper.BuildConfig 14 | import top.ltfan.notdeveloper.Item 15 | 16 | @Keep 17 | class Hook : IXposedHookLoadPackage { 18 | override fun handleLoadPackage(lpparam: LoadPackageParam) { 19 | if (lpparam.packageName.startsWith("android") || lpparam.packageName.startsWith("com.android")) { 20 | return 21 | } 22 | 23 | Log.d("processing package ${lpparam.packageName}") 24 | 25 | if (lpparam.packageName == BuildConfig.APPLICATION_ID) { 26 | XposedHelpers.findAndHookMethod( 27 | "${BuildConfig.APPLICATION_ID}.xposed.ModuleStatusKt", 28 | lpparam.classLoader, 29 | "getStatusIsModuleActivated", 30 | object : XC_MethodHook() { 31 | override fun beforeHookedMethod(param: MethodHookParam) { 32 | param.result = true 33 | } 34 | }, 35 | ) 36 | } 37 | 38 | val prefs = XSharedPreferences(BuildConfig.APPLICATION_ID) 39 | 40 | val newApiCallback = object : XC_MethodHook() { 41 | override fun beforeHookedMethod(param: MethodHookParam) { 42 | prefs.reload() 43 | changeResultToZero( 44 | lpparam, 45 | prefs, 46 | param, 47 | *(Item.oldApiItems.toTypedArray() + Item.newApiItems.toTypedArray()) 48 | ) 49 | } 50 | } 51 | 52 | val oldApiCallback = object : XC_MethodHook() { 53 | override fun beforeHookedMethod(param: MethodHookParam) { 54 | prefs.reload() 55 | changeResultToZero(lpparam, prefs, param, *Item.oldApiItems.toTypedArray()) 56 | } 57 | } 58 | 59 | XposedHelpers.findAndHookMethod( 60 | Settings.Global::class.java, 61 | "getInt", 62 | ContentResolver::class.java, 63 | String::class.java, 64 | Int::class.java, 65 | newApiCallback, 66 | ) 67 | 68 | XposedHelpers.findAndHookMethod( 69 | Settings.Global::class.java, 70 | "getInt", 71 | ContentResolver::class.java, 72 | String::class.java, 73 | newApiCallback, 74 | ) 75 | 76 | XposedHelpers.findAndHookMethod( 77 | Settings.Secure::class.java, 78 | "getInt", 79 | ContentResolver::class.java, 80 | String::class.java, 81 | Int::class.java, 82 | oldApiCallback, 83 | ) 84 | 85 | XposedHelpers.findAndHookMethod( 86 | Settings.Secure::class.java, 87 | "getInt", 88 | ContentResolver::class.java, 89 | String::class.java, 90 | oldApiCallback, 91 | ) 92 | 93 | processSystemProps(prefs, lpparam) 94 | } 95 | 96 | private fun processSystemProps(prefs: XSharedPreferences, lpparam: LoadPackageParam) { 97 | val clazz = XposedHelpers.findClassIfExists( 98 | "android.os.SystemProperties", lpparam.classLoader 99 | ) 100 | 101 | if (clazz == null) { 102 | Log.w("cannot find SystemProperties class") 103 | return 104 | } 105 | 106 | val ffsReady = "sys.usb.ffs.ready" 107 | val usbState = "sys.usb.state" 108 | val usbConfig = "sys.usb.config" 109 | val rebootFunc = "persist.sys.usb.reboot.func" 110 | val svcAdbd = "init.svc.adbd" 111 | 112 | val methodGet = "get" 113 | val methodGetprop = "getprop" 114 | val methodGetBoolean = "getBoolean" 115 | val methodGetInt = "getInt" 116 | val methodGetLong = "getLong" 117 | 118 | val overrideAdb = "mtp" 119 | val overrideSvcAdbd = "stopped" 120 | 121 | listOf(methodGet, methodGetprop, methodGetBoolean, methodGetInt, methodGetLong).forEach { 122 | XposedBridge.hookAllMethods( 123 | clazz, it, 124 | object : XC_MethodHook() { 125 | override fun beforeHookedMethod(param: MethodHookParam) { 126 | prefs.reload() 127 | if (!prefs.getBoolean(Item.AdbEnabled.key, true)) return 128 | 129 | val arg = param.args[0] as String 130 | Log.d("processing ${param.method.name} from ${lpparam.packageName} with arg $arg") 131 | 132 | if (param.method.name != methodGet && arg != ffsReady) { 133 | Log.i("props processed ${param.method.name} from ${lpparam.packageName} receiving invalid arg $arg") 134 | return 135 | } 136 | 137 | when (arg) { 138 | ffsReady -> { 139 | when (param.method.name) { 140 | methodGet -> param.result = "0" 141 | methodGetprop -> param.result = "0" 142 | methodGetBoolean -> param.result = false 143 | methodGetInt -> param.result = 0 144 | methodGetLong -> param.result = 0L 145 | } 146 | } 147 | 148 | usbState -> param.result = overrideAdb 149 | usbConfig -> param.result = overrideAdb 150 | rebootFunc -> param.result = overrideAdb 151 | svcAdbd -> param.result = overrideSvcAdbd 152 | } 153 | 154 | Log.d("processed ${param.method.name}($arg): ${param.result}") 155 | } 156 | } 157 | ) 158 | } 159 | } 160 | 161 | private fun changeResultToZero( 162 | lpparam: LoadPackageParam, 163 | prefs: XSharedPreferences, 164 | param: MethodHookParam, 165 | vararg items: Item 166 | ) { 167 | val arg = param.args[1] as String 168 | Log.d("processing ${param.method.name} from ${lpparam.packageName} with arg $arg") 169 | 170 | items.forEach { item -> 171 | val key = item.key 172 | if (prefs.getBoolean(key, true) && arg == key) { 173 | param.result = 0 174 | Log.d("processed ${param.method.name}($arg): ${param.result}") 175 | return 176 | } 177 | } 178 | 179 | Log.d("processed ${param.method.name}($arg) without changing result") 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/xposed/Log.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.xposed 2 | 3 | import de.robv.android.xposed.XposedBridge 4 | 5 | object Log { 6 | const val TAG = "NotDeveloper" 7 | 8 | fun d(message: String, throwable: Throwable? = null) { 9 | log("DEBUG", message, throwable) 10 | } 11 | 12 | fun i(message: String, throwable: Throwable? = null) { 13 | log("INFO", message, throwable) 14 | } 15 | 16 | fun w(message: String, throwable: Throwable? = null) { 17 | log("WARN", message, throwable) 18 | } 19 | 20 | private fun log(level: String, message: String, throwable: Throwable? = null) { 21 | XposedBridge.log("[$level] $TAG: $message") 22 | if (throwable != null) { 23 | XposedBridge.log(throwable) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/kotlin/top/ltfan/notdeveloper/xposed/ModuleStatus.kt: -------------------------------------------------------------------------------- 1 | package top.ltfan.notdeveloper.xposed 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import androidx.annotation.Keep 6 | 7 | val statusIsModuleActivated 8 | @Keep get() = false 9 | 10 | 11 | val Context.statusIsPreferencesReady: Boolean 12 | @SuppressLint("WorldReadableFiles")get() { 13 | return try { 14 | @Suppress("DEPRECATION") getSharedPreferences( 15 | "testPreferences", 16 | Context.MODE_WORLD_READABLE 17 | ) 18 | true 19 | } catch (t: Throwable) { 20 | android.util.Log.e(Log.TAG, "failed to confirm SharedPreferences' state.", t) 21 | false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.kotlin) apply false 4 | alias(libs.plugins.androidApplication) apply false 5 | alias(libs.plugins.androidLibrary) apply false 6 | alias(libs.plugins.composeCompiler) apply false 7 | } 8 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | app-versionName = "1.5.0" 3 | app-versionCode = "9" 4 | kotlin = "2.1.20" 5 | agp = "8.10.0" 6 | compileSdk = "36" 7 | targetSdk = "36" 8 | minSdk = "27" 9 | compose = "2025.05.00" 10 | lifecycle = "2.9.0" 11 | activity = "1.10.1" 12 | preference = "1.2.1" 13 | xposed-api = "82" 14 | 15 | [libraries] 16 | kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } 17 | lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } 18 | lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" } 19 | activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } 20 | compose = { module = "androidx.compose:compose-bom", version.ref = "compose" } 21 | compose-runtime = { module = "androidx.compose.runtime:runtime" } 22 | compose-foundation = { module = "androidx.compose.foundation:foundation" } 23 | compose-ui = { module = "androidx.compose.ui:ui" } 24 | compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } 25 | compose-animation = { module = "androidx.compose.animation:animation" } 26 | compose-material3 = { module = "androidx.compose.material3:material3" } 27 | preference = { module = "androidx.preference:preference-ktx", version.ref = "preference" } 28 | xposed-api = { module = "de.robv.android.xposed:api", version.ref = "xposed-api" } 29 | 30 | [plugins] 31 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 32 | androidApplication = { id = "com.android.application", version.ref = "agp" } 33 | androidLibrary = { id = "com.android.library", version.ref = "agp" } 34 | composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 35 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfqwdsj/IAmNotADeveloper/af28e73a6f048d9ec61795c21798b8093125c272/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 24 13:04:41 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "IAmNotADeveloper" 2 | 3 | pluginManagement { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | gradlePluginPortal() 8 | } 9 | } 10 | dependencyResolutionManagement { 11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 12 | repositories { 13 | google() 14 | mavenCentral() 15 | maven("https://api.xposed.info/") 16 | } 17 | } 18 | 19 | include(":app") 20 | --------------------------------------------------------------------------------