[
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non: [push, pull_request]\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v1\n\n      - name: Set up JDK 17\n        uses: actions/setup-java@v1\n        with:\n          java-version: 17\n\n      - name: Grant Permission to Execute\n        run: chmod +x gradlew\n\n      - name: Build with Gradle\n        uses: gradle/gradle-build-action@v2\n        with:\n          arguments: build"
  },
  {
    "path": ".github/workflows/publish_docs.yml",
    "content": "name: Build documentation\n\non:\n  push:\n    branches: [\"main\"]\n  workflow_dispatch:\n\npermissions:\n  id-token: write\n  pages: write\n\nenv:\n  INSTANCE: 'Writerside/in'\n  ARTIFACT: 'webHelpIN2-all.zip'\n  DOCKER_VERSION: '241.15989'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          sparse-checkout: |\n            .github\n            docs\n\n      - name: Setup docs\n        run: mv docs/* .\n\n      - name: Build docs using Writerside Docker builder\n        uses: JetBrains/writerside-github-action@v4\n        with:\n          instance: ${{ env.INSTANCE }}\n          artifact: ${{ env.ARTIFACT }}\n          docker-version: ${{ env.DOCKER_VERSION }}\n\n      - name: Save artifact with build results\n        uses: actions/upload-artifact@v4\n        with:\n          name: docs\n          path: |\n            artifacts/${{ env.ARTIFACT }}\n            artifacts/report.json\n          retention-days: 7\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    needs: [build]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: docs\n\n      - name: Unzip artifact\n        run: unzip -O UTF-8 -qq '${{ env.ARTIFACT }}' -d dir\n\n      - name: Setup Pages\n        uses: actions/configure-pages@v4\n\n      - name: Package and upload Pages artifact\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: dir\n\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v1\n\n      - name: Set up JDK 17\n        uses: actions/setup-java@v1\n        with:\n          java-version: 17\n\n      - name: Grant Permission to Execute\n        run: chmod +x gradlew\n\n      - name: Build with Gradle\n        uses: gradle/gradle-build-action@v2\n        with:\n          arguments: build\n\n      - name: Publish Library on Maven Central\n        run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache\n        env:\n          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}\n          ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }}\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}\n\n      - name: Publish Gradle Plugin\n        run: |\n          echo \"Publishing Gradle Plugin🚀\"\n          ./gradlew :gradle-plugin:publishPlugins\n        env:\n          GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PLUGIN_PORTAL_KEY }}\n          GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PLUGIN_PORTAL_SECRET }}"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n/.idea/\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at shreyaspatilg@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Feeling Awesome! Thanks for thinking about this.\n\nYou can contribute us by filing issues, bugs and PRs. You can also take a look at active issues and fix them.\n\nIf you want to discuss on something then feel free to present your opinions, views or any other relevant comment on [discussions](https://github.com/PatilShreyas/bytemask/discussions). \n\n### Code contribution\n\n- Open issue regarding proposed change.\n- If your proposed change is approved, Fork this repo and do changes.\n- Open PR against latest *development* branch. Add nice description in PR.\n- You're done!\n\n### Code contribution checklist\n\n- New code addition/deletion should not break existing flow of a system.\n- All tests should be passed.\n- Verify `./gradlew build` is passing before raising a PR.\n- Reformat code with Spotless `./gradlew spotlessApply` before raising a PR.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2024 Shreyas Patil\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Bytemask\n\nAndroid Gradle Plugin that masks your **secret strings** for the app in the source code making it difficult to extract from _reverse engineering_.\n\n[***▶️ Read the documentation***](https://patilshreyas.github.io/bytemask) for more information and guide to use this utility\n\n[![Gradle Plugin](https://img.shields.io/gradle-plugin-portal/v/dev.shreyaspatil.bytemask.plugin?color=%233cafc6&label=Plugin&logo=gradle&style=flat-square)](https://plugins.gradle.org/plugin/dev.shreyaspatil.bytemask.plugin)\n[![Build](https://github.com/PatilShreyas/Bytemask/actions/workflows/build.yml/badge.svg)](https://github.com/PatilShreyas/Bytemask/actions/workflows/build.yml)\n[![Release](https://github.com/PatilShreyas/Bytemask/actions/workflows/release.yml/badge.svg)](https://github.com/PatilShreyas/Bytemask/actions/workflows/release.yml)\n\n## 🙋‍♂️ Contribute\n\nRead [contribution guidelines](CONTRIBUTING.md) for more information regarding contribution.\n\n## 💬 Discuss?\n\nHave any questions, doubts or want to present your opinions, views? You're always welcome. You\ncan [start discussions](https://github.com/PatilShreyas/bytemask/discussions).\n\n## License\n\n```\nCopyright 2024 Shreyas Patil\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\n\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build"
  },
  {
    "path": "app/build.gradle.kts",
    "content": "plugins {\n    id(\"com.android.application\")\n    id(\"org.jetbrains.kotlin.android\")\n    id(\"dev.shreyaspatil.bytemask.plugin\") version \"1.0.0-alpha02\"\n}\n\nandroid {\n    namespace = \"dev.shreyaspatil.bytemask.example\"\n    compileSdk = 34\n\n    defaultConfig {\n        applicationId = \"dev.shreyaspatil.bytemask.example\"\n        minSdk = 24\n        targetSdk = 34\n        versionCode = 1\n        versionName = \"1.0\"\n\n        testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n        vectorDrawables { useSupportLibrary = true }\n    }\n\n    buildTypes {\n        release {\n            isMinifyEnabled = true\n            proguardFiles(\n                getDefaultProguardFile(\"proguard-android-optimize.txt\"),\n                \"proguard-rules.pro\"\n            )\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_1_8\n        targetCompatibility = JavaVersion.VERSION_1_8\n    }\n    kotlinOptions { jvmTarget = \"1.8\" }\n    buildFeatures { viewBinding { enable = true } }\n    packaging { resources { excludes += \"/META-INF/{AL2.0,LGPL2.1}\" } }\n    signingConfigs {\n        named(\"debug\") { storeFile = rootProject.file(\"debug.keystore\") }\n        create(\"release\") { initWith(getByName(\"debug\")) }\n    }\n}\n\ndependencies {\n    implementation(\"androidx.core:core-ktx:1.12.0\")\n    implementation(\"androidx.lifecycle:lifecycle-runtime-ktx:2.7.0\")\n    implementation(\"androidx.activity:activity-ktx:1.8.2\")\n    testImplementation(\"junit:junit:4.13.2\")\n    androidTestImplementation(\"androidx.test.ext:junit:1.1.5\")\n    androidTestImplementation(\"androidx.test.espresso:espresso-core:3.5.1\")\n}\n\nbytemaskConfig {\n    // UNCOMMENT THIS: To set the generated class name as \"MyAppConfig\"\n    // className.set(\"MyAppConfig\")\n\n    // UNCOMMENT THIS: To pick secrets from the `secrets.properties`\n    // defaultPropertiesFileName.set(\"secrets.properties\")\n\n    // For debug variant, enable encryption with a debug SHA-256 key.\n    configure(\"debug\") { enableEncryption.set(true) }\n\n    configure(\"release\") { enableEncryption.set(true) }\n}\n"
  },
  {
    "path": "app/bytemask.properties",
    "content": "API_KEY=Hello1234\nACCESS_TOKEN=ACCESS_TOKEN_HERE\nAPI_ENDPOINT=https://example.com"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:dataExtractionRules=\"@xml/data_extraction_rules\"\n        android:fullBackupContent=\"@xml/backup_rules\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.ByteMask\"\n        tools:targetApi=\"31\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\"\n            android:label=\"@string/app_name\"\n            android:theme=\"@style/Theme.ByteMask\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/dev/shreyaspatil/bytemask/example/MainActivity.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.example\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport dev.shreyaspatil.bytemask.example.databinding.ActivityMainBinding\n\nclass MainActivity : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        with(binding) {\n            buttonApiKey.setOnClickListener { setSecret(BytemaskConfig.API_KEY) }\n            buttonAccessToken.setOnClickListener { setSecret(BytemaskConfig.ACCESS_TOKEN) }\n            buttonApiEndpoint.setOnClickListener { setSecret(BytemaskConfig.API_ENDPOINT) }\n        }\n    }\n\n    private fun ActivityMainBinding.setSecret(secret: String) {\n        textView.text = secret\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\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:orientation=\"vertical\"\n    android:padding=\"16dp\">\n\n    <TextView\n        android:id=\"@+id/textView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/black\"\n        android:fontFamily=\"monospace\"\n        android:gravity=\"center\"\n        android:padding=\"16dp\"\n        android:text=\"Secrets will appear here\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"18sp\" />\n\n    <Button\n        android:id=\"@+id/buttonApiKey\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"Show API Key\" />\n\n    <Button\n        android:id=\"@+id/buttonAccessToken\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Show Access Token\" />\n\n    <Button\n        android:id=\"@+id/buttonApiEndpoint\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Show API Endpoint\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_200\">#FFBB86FC</color>\n    <color name=\"purple_500\">#FF6200EE</color>\n    <color name=\"purple_700\">#FF3700B3</color>\n    <color name=\"teal_200\">#FF03DAC5</color>\n    <color name=\"teal_700\">#FF018786</color>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">ByteMask</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"Theme.ByteMask\" parent=\"android:Theme.Material.Light.NoActionBar\" />\n</resources>"
  },
  {
    "path": "app/src/main/res/xml/backup_rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n   Sample backup rules file; uncomment and customize as necessary.\n   See https://developer.android.com/guide/topics/data/autobackup\n   for details.\n   Note: This file is ignored for devices older that API 31\n   See https://developer.android.com/about/versions/12/backup-restore\n-->\n<full-backup-content>\n    <!--\n   <include domain=\"sharedpref\" path=\".\"/>\n   <exclude domain=\"sharedpref\" path=\"device.xml\"/>\n-->\n</full-backup-content>"
  },
  {
    "path": "app/src/main/res/xml/data_extraction_rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n   Sample data extraction rules file; uncomment and customize as necessary.\n   See https://developer.android.com/about/versions/12/backup-restore#xml-changes\n   for details.\n-->\n<data-extraction-rules>\n    <cloud-backup>\n        <!-- TODO: Use <include> and <exclude> to control what is backed up.\n        <include .../>\n        <exclude .../>\n        -->\n    </cloud-backup>\n    <!--\n    <device-transfer>\n        <include .../>\n        <exclude .../>\n    </device-transfer>\n    -->\n</data-extraction-rules>"
  },
  {
    "path": "build.gradle.kts",
    "content": "plugins {\n    alias(libs.plugins.android.app) apply false\n    alias(libs.plugins.android.library) apply false\n    alias(libs.plugins.kotlin.android) apply false\n    alias(libs.plugins.kotlin.jvm) apply false\n    alias(libs.plugins.spotless) apply false\n}\n\nsubprojects {\n    apply(plugin = rootProject.libs.plugins.spotless.get().pluginId)\n    configure<com.diffplug.gradle.spotless.SpotlessExtension> {\n        kotlin {\n            target(\"**/*.kt\")\n            targetExclude(\"$buildDir/**/*.kt\")\n            targetExclude(\"bin/**/*.kt\")\n            ktfmt().kotlinlangStyle()\n            licenseHeaderFile(rootProject.file(\"spotless/copyright.kt\"))\n        }\n        kotlinGradle {\n            target(\"**/*.gradle.kts\")\n            ktfmt().kotlinlangStyle()\n        }\n    }\n}"
  },
  {
    "path": "bytemask-android/.gitignore",
    "content": "/build"
  },
  {
    "path": "bytemask-android/build.gradle.kts",
    "content": "@Suppress(\"DSL_SCOPE_VIOLATION\") // TODO: Remove once KTIJ-19369 is fixed\nplugins {\n    alias(libs.plugins.android.library)\n    alias(libs.plugins.kotlin.android)\n    alias(libs.plugins.mavenPublish)\n}\n\nandroid {\n    namespace = \"dev.shreyaspatil.bytemask.android\"\n    compileSdk = 34\n\n    defaultConfig {\n        minSdk = 21\n\n        testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n        consumerProguardFiles(\"consumer-rules.pro\")\n    }\n\n    buildTypes {\n        release {\n            isMinifyEnabled = false\n            proguardFiles(\n                getDefaultProguardFile(\"proguard-android-optimize.txt\"),\n                \"proguard-rules.pro\"\n            )\n        }\n    }\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_1_8\n        targetCompatibility = JavaVersion.VERSION_1_8\n    }\n    kotlinOptions { jvmTarget = \"1.8\" }\n}\n\ndependencies {\n    api(project(\":bytemask-core\"))\n    implementation(libs.appStartup)\n    compileOnly(libs.annotation.jvm)\n\n    testImplementation(libs.junit)\n    androidTestImplementation(libs.androidx.test.ext.junit)\n    androidTestImplementation(libs.espresso.core)\n}\n"
  },
  {
    "path": "bytemask-android/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "bytemask-android/gradle.properties",
    "content": "POM_ARTIFACT_ID=bytemask-android\nPOM_NAME=Bytemask - Android\nPOM_DESCRIPTION=Generates masked text resource."
  },
  {
    "path": "bytemask-android/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "bytemask-android/src/androidTest/java/dev/shreyaspatil/bytemask/android/ExampleInstrumentedTest.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.android\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Assert.*\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"dev.shreyaspatil.bytemask.android.test\", appContext.packageName)\n    }\n}\n"
  },
  {
    "path": "bytemask-android/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <application>\n        <provider\n            android:name=\"androidx.startup.InitializationProvider\"\n            android:authorities=\"${applicationId}.androidx-startup\"\n            android:exported=\"false\"\n            tools:node=\"merge\">\n            <meta-data  android:name=\"dev.shreyaspatil.bytemask.android.initializer.BytemaskInitializer\"\n                android:value=\"androidx.startup\" />\n        </provider>\n    </application>\n</manifest>"
  },
  {
    "path": "bytemask-android/src/main/java/dev/shreyaspatil/bytemask/android/AndroidBytemask.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.android\n\nimport android.content.Context\nimport dev.shreyaspatil.bytemask.android.impl.AppSigningKeyAsEncryptionKeyProvider\nimport dev.shreyaspatil.bytemask.core.Bytemask\n\n/** A helper class for initializing Bytemask in android environment */\nobject AndroidBytemask {\n    /** Initializes Bytemask */\n    @JvmStatic\n    fun init(context: Context) {\n        Bytemask.init(AppSigningKeyAsEncryptionKeyProvider(context))\n    }\n}\n"
  },
  {
    "path": "bytemask-android/src/main/java/dev/shreyaspatil/bytemask/android/impl/AppSigningKeyAsEncryptionKeyProvider.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.android.impl\n\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.content.pm.Signature\nimport android.os.Build\nimport androidx.annotation.RequiresApi\nimport dev.shreyaspatil.bytemask.core.EncryptionKeyProvider\nimport dev.shreyaspatil.bytemask.core.encryption.Sha256DigestableKey\nimport java.security.MessageDigest\n\n/**\n * A class that provides the SHA-256 digest of the app's signing key.\n *\n * This class implements the AppSigningKeyInfoProvider interface.\n *\n * @property context The application context.\n */\ninternal class AppSigningKeyAsEncryptionKeyProvider(private val context: Context) :\n    EncryptionKeyProvider {\n\n    private val sha256 =\n        Sha256DigestableKey(\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                    getSha256SignatureApi28Impl()\n                } else {\n                    getSha256SignaturePreApi28Impl()\n                }\n                .uppercase()\n        )\n\n    /**\n     * Returns the SHA-256 digest of the app's signing key.\n     *\n     * This function checks the Android version and uses the appropriate method to retrieve the\n     * app's signing key. The signing key is then hashed using SHA-256 and returned as a\n     * Sha256DigestableKey.\n     *\n     * @return The SHA-256 digest of the app's signing key as a Sha256DigestableKey.\n     */\n    override fun get(): Sha256DigestableKey {\n        return sha256\n    }\n\n    /**\n     * Retrieves the SHA-256 digest of the app's signing key for Android versions below API 28.\n     *\n     * @return The SHA-256 digest of the app's signing key as a string.\n     */\n    @Suppress(\"DEPRECATION\")\n    private fun getSha256SignaturePreApi28Impl(): String {\n        val packageInfo =\n            context.packageManager.getPackageInfo(\n                context.packageName,\n                PackageManager.GET_SIGNATURES\n            )\n        val signatures = packageInfo.signatures\n        return retrieveSha256(signatures)\n    }\n\n    /**\n     * Retrieves the SHA-256 digest of the app's signing key for Android versions API 28 and above.\n     *\n     * This function uses the GET_SIGNING_CERTIFICATES flag, which is available from API 28.\n     *\n     * @return The SHA-256 digest of the app's signing key as a string.\n     */\n    @RequiresApi(Build.VERSION_CODES.P)\n    private fun getSha256SignatureApi28Impl(): String {\n        val packageInfo =\n            context.packageManager.getPackageInfo(\n                context.packageName,\n                PackageManager.GET_SIGNING_CERTIFICATES\n            )\n        val signatures = packageInfo.signingInfo.apkContentsSigners\n        return retrieveSha256(signatures)\n    }\n\n    /**\n     * Retrieves the SHA-256 digest of the given signatures.\n     *\n     * This function hashes each signature using SHA-256 and concatenates the results into a string.\n     *\n     * @param signatures The signatures to hash.\n     * @return The SHA-256 digest of the signatures as a string.\n     */\n    private fun retrieveSha256(signatures: Array<Signature>): String {\n        val md = MessageDigest.getInstance(\"SHA-256\")\n        for (signature in signatures) {\n            md.update(signature.toByteArray())\n        }\n        val digest = md.digest()\n        val hexString = StringBuilder()\n        for (byte in digest) {\n            hexString.append(String.format(\"%02X\", byte))\n        }\n        return hexString.toString()\n    }\n}\n"
  },
  {
    "path": "bytemask-android/src/main/java/dev/shreyaspatil/bytemask/android/initializer/BytemaskInitializer.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.android.initializer\n\nimport android.content.Context\nimport androidx.startup.Initializer\nimport dev.shreyaspatil.bytemask.android.AndroidBytemask\nimport dev.shreyaspatil.bytemask.core.Bytemask\n\n/** Initializes ByteMask at the application startup */\nclass BytemaskInitializer : Initializer<Bytemask> {\n    override fun create(context: Context): Bytemask {\n        AndroidBytemask.init(context)\n        return Bytemask.getInstance()\n    }\n\n    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()\n}\n"
  },
  {
    "path": "bytemask-android/src/test/java/dev/shreyaspatil/bytemask/android/ExampleUnitTest.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.android\n\nimport org.junit.Assert.*\nimport org.junit.Test\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}\n"
  },
  {
    "path": "bytemask-core/.gitignore",
    "content": "/build"
  },
  {
    "path": "bytemask-core/build.gradle.kts",
    "content": "@Suppress(\"DSL_SCOPE_VIOLATION\") // TODO: Remove once KTIJ-19369 is fixed\nplugins {\n    alias(libs.plugins.kotlin.jvm)\n    alias(libs.plugins.mavenPublish)\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17\n    targetCompatibility = JavaVersion.VERSION_17\n}\n"
  },
  {
    "path": "bytemask-core/gradle.properties",
    "content": "POM_ARTIFACT_ID=bytemask-core\nPOM_NAME=Bytemask - Core\nPOM_DESCRIPTION=Generates masked text resource."
  },
  {
    "path": "bytemask-core/src/main/java/dev/shreyaspatil/bytemask/core/Bytemask.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.core\n\nimport dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec\nimport dev.shreyaspatil.bytemask.core.encryption.asEncryptedValue\nimport dev.shreyaspatil.bytemask.core.encryption.asPlainValue\nimport dev.shreyaspatil.bytemask.core.encryption.decrypt\nimport dev.shreyaspatil.bytemask.core.encryption.encrypt\n\n/**\n * ByteMask is a class that provides functionality for masking and unmasking strings. It uses an\n * [EncryptionKeyProvider] to get the key for encryption and decryption.\n *\n * @property encryptionKeyProvider used to get the key for encryption and decryption.\n */\nclass Bytemask private constructor(private val encryptionKeyProvider: EncryptionKeyProvider) {\n\n    /**\n     * Masks a string by encrypting it.\n     *\n     * @param encryptionSpec The details of the encryption, such as the algorithm and mode.\n     * @param value The string to be masked.\n     * @return The masked string.\n     */\n    fun mask(encryptionSpec: EncryptionSpec, value: String): String {\n        return value\n            .asPlainValue()\n            .encrypt(detail = encryptionSpec, key = encryptionKeyProvider.get())\n            .value\n    }\n\n    /**\n     * Unmasks a string by decrypting it.\n     *\n     * @param encryptionSpec The details of the encryption, such as the algorithm and mode.\n     * @param value The string to be unmasked.\n     * @return The unmasked string.\n     */\n    fun unmask(encryptionSpec: EncryptionSpec, value: String): String {\n        return value\n            .asEncryptedValue()\n            .decrypt(detail = encryptionSpec, key = encryptionKeyProvider.get())\n            .value\n    }\n\n    companion object {\n        @Volatile private var INSTANCE: Bytemask? = null\n\n        /**\n         * Initializes the ByteMask.\n         *\n         * @param encryptionKeyProvider An instance of [EncryptionKeyProvider] used to get the key\n         *   for encryption and decryption.\n         */\n        fun init(encryptionKeyProvider: EncryptionKeyProvider) {\n            if (INSTANCE != null) return\n\n            synchronized(this) {\n                if (INSTANCE == null) {\n                    val byteMask = Bytemask(encryptionKeyProvider = encryptionKeyProvider)\n                    INSTANCE = byteMask\n                }\n            }\n        }\n\n        /**\n         * Returns the instance of ByteMask.\n         *\n         * @return The singleton instance of ByteMask.\n         * @throws IllegalStateException If the ByteMask instance has not been initialized.\n         */\n        fun getInstance(): Bytemask {\n            return INSTANCE\n                ?: synchronized(this) {\n                    INSTANCE ?: throw IllegalStateException(\"ByteMask is not initialized.\")\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "bytemask-core/src/main/java/dev/shreyaspatil/bytemask/core/EncryptionKeyProvider.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.core\n\nimport dev.shreyaspatil.bytemask.core.encryption.Sha256DigestableKey\n\n/** Provides information about the App Signing Key. */\ninterface EncryptionKeyProvider {\n    /** Returns the SHA-256 from the app signing information. */\n    fun get(): Sha256DigestableKey\n}\n"
  },
  {
    "path": "bytemask-core/src/main/java/dev/shreyaspatil/bytemask/core/encryption/EncryptionSpec.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.core.encryption\n\nimport java.io.Serializable\n\nclass EncryptionSpec(val algorithm: String, val transformation: String) : Serializable\n"
  },
  {
    "path": "bytemask-core/src/main/java/dev/shreyaspatil/bytemask/core/encryption/EncryptionUtils.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.core.encryption\n\nimport java.security.SecureRandom\nimport javax.crypto.Cipher\nimport javax.crypto.spec.IvParameterSpec\nimport javax.crypto.spec.SecretKeySpec\nimport kotlin.io.encoding.Base64\nimport kotlin.io.encoding.ExperimentalEncodingApi\n\n@OptIn(ExperimentalEncodingApi::class)\n/** This object provides utility functions for AES encryption and decryption. */\nobject EncryptionUtils {\n    /**\n     * Encrypts a string using AES encryption.\n     *\n     * @param key The encryption key as a string. It should be hashed with SHA-256.\n     * @param value The string to encrypt.\n     * @return The encrypted string.\n     */\n    fun encrypt(\n        detail: EncryptionSpec,\n        key: Sha256DigestableKey,\n        plain: Value.Plain\n    ): Value.Encrypted {\n        // Generate a random Initialization Vector (IV)\n        val iv = ByteArray(16)\n        SecureRandom().nextBytes(iv)\n\n        // Generate a SecretKeySpec from the SHA-256 hashed key\n        val secretKeySpec = SecretKeySpec(key.digest(), detail.algorithm)\n\n        // Create an IvParameterSpec from the IV\n        val ivParameterSpec = IvParameterSpec(iv)\n\n        // Get an instance of the Cipher\n        val cipher = Cipher.getInstance(detail.transformation)\n\n        // Initialize the Cipher in ENCRYPT_MODE with the SecretKeySpec and IvParameterSpec\n        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)\n\n        // Encrypt the value and return it as a Base64 encoded string\n        val encryptedValue = cipher.doFinal(plain.value.toByteArray())\n        return Base64.Default.encode(iv + encryptedValue).asEncryptedValue()\n    }\n\n    /**\n     * Decrypts an AES encrypted string.\n     *\n     * @param key The encryption key as a string. It should be hashed with SHA-256.\n     * @param encrypted The encrypted string to decrypt.\n     * @return The decrypted string.\n     */\n    fun decrypt(\n        detail: EncryptionSpec,\n        key: Sha256DigestableKey,\n        encrypted: Value.Encrypted\n    ): Value.Plain {\n        // Decode the encrypted string from Base64 to ByteArray\n        val decoded = Base64.Default.decode(encrypted.value)\n\n        // Extract the Initialization Vector (IV) from the decoded ByteArray\n        val iv = decoded.copyOfRange(0, 16)\n\n        // Extract the encrypted value from the decoded ByteArray\n        val encryptedValue = decoded.copyOfRange(16, decoded.size)\n\n        // Generate a SecretKeySpec from the SHA-256 hashed key\n        val secretKeySpec = SecretKeySpec(key.digest(), detail.algorithm)\n\n        // Create an IvParameterSpec from the IV\n        val ivParameterSpec = IvParameterSpec(iv)\n\n        // Get an instance of the Cipher\n        val cipher = Cipher.getInstance(detail.transformation)\n\n        // Initialize the Cipher in DECRYPT_MODE with the SecretKeySpec and IvParameterSpec\n        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)\n\n        // Decrypt the encrypted value and return it as a string\n        return cipher.doFinal(encryptedValue).toString(Charsets.UTF_8).asPlainValue()\n    }\n}\n\n/**\n * Encrypts the value.\n *\n * This function uses the EncryptionUtils.encrypt function to encrypt the value.\n *\n * @param key The encryption key as a Sha256DigestableKey.\n * @return The encrypted value as a Value.Encrypted.\n */\ninline fun Value.Plain.encrypt(detail: EncryptionSpec, key: Sha256DigestableKey) =\n    EncryptionUtils.encrypt(detail, key, this)\n\n/**\n * Decrypts the value.\n *\n * This function uses the EncryptionUtils.decrypt function to decrypt the value.\n *\n * @param key The encryption key as a Sha256DigestableKey.\n * @return The decrypted value as a Value.Plain.\n */\ninline fun Value.Encrypted.decrypt(detail: EncryptionSpec, key: Sha256DigestableKey) =\n    EncryptionUtils.decrypt(detail, key, this)\n"
  },
  {
    "path": "bytemask-core/src/main/java/dev/shreyaspatil/bytemask/core/encryption/Sha256DigestableKey.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.core.encryption\n\nimport java.io.Serializable\nimport java.security.MessageDigest\n\n/**\n * A value class that represents a key that can be digested using SHA-256.\n *\n * @property value The string value of the key.\n */\n@JvmInline\nvalue class Sha256DigestableKey(private val value: String) : Serializable {\n\n    /**\n     * Digests the key using SHA-256.\n     *\n     * @return The SHA-256 hash of the key as a ByteArray.\n     */\n    fun digest(): ByteArray {\n        // Get an instance of the MessageDigest with SHA-256\n        val digest = MessageDigest.getInstance(\"SHA-256\")\n\n        // Return the SHA-256 hash of the string\n        return digest.digest(value.toByteArray(Charsets.UTF_8))\n    }\n}\n"
  },
  {
    "path": "bytemask-core/src/main/java/dev/shreyaspatil/bytemask/core/encryption/Value.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.core.encryption\n\n/**\n * A interface for a value that can be either plain or encrypted.\n *\n * @property value The string value of the Value.\n */\nsealed interface Value {\n    val value: String\n\n    /**\n     * A data class representing a plain (unencrypted) value.\n     *\n     * @property value The string value of the plain value.\n     */\n    data class Plain(override val value: String) : Value\n\n    /**\n     * A data class representing an encrypted value.\n     *\n     * @property value The string value of the encrypted value.\n     */\n    data class Encrypted(override val value: String) : Value\n}\n\n/**\n * Converts a String to a Plain Value.\n *\n * @return A Plain Value with this string as the value.\n */\nfun String.asPlainValue() = Value.Plain(this)\n\n/**\n * Converts a String to an Encrypted Value.\n *\n * @return An Encrypted Value with this string as the value.\n */\nfun String.asEncryptedValue() = Value.Encrypted(this)\n"
  },
  {
    "path": "docs/.idea/.gitignore",
    "content": "# Default ignored files\n/shelf/\n/workspace.xml\n"
  },
  {
    "path": "docs/.idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/.idea/docs.iml\" filepath=\"$PROJECT_DIR$/.idea/docs.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": "docs/.idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$/..\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "docs/Writerside/c.list",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE categories\n        SYSTEM \"https://resources.jetbrains.com/writerside/1.0/categories.dtd\">\n<categories>\n    <category id=\"wrs\" name=\"Writerside documentation\" order=\"1\"/>\n</categories>"
  },
  {
    "path": "docs/Writerside/cfg/buildprofiles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<buildprofiles xsi:noNamespaceSchemaLocation=\"https://resources.jetbrains.com/writerside/1.0/build-profiles.xsd\"\n               xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\n    <variables>\n        <download-page>https://github.com/PatilShreyas/bytemask</download-page>\n        <download-title>GitHub</download-title>\n        <showDownloadButton>true</showDownloadButton>\n        <enable-browser-edits>true</enable-browser-edits>\n        <browser-edits-url>https://github.com/PatilShreyas/bytemask/tree/main/docs/Writerside/</browser-edits-url>\n        <custom-favicons>https://raw.githubusercontent.com/PatilShreyas/bytemask/main/icon.png</custom-favicons>\n        <header-logo>https://raw.githubusercontent.com/PatilShreyas/bytemask/main/icon.png</header-logo>\n    </variables>\n\n    <build-profile instance=\"in\">\n        <variables>\n            <noindex-content>false</noindex-content>\n            <og-image>https://raw.githubusercontent.com/PatilShreyas/bytemask/main/cover.png</og-image>\n            <og-twitter>https://raw.githubusercontent.com/PatilShreyas/bytemask/main/cover.png</og-twitter>\n        </variables>\n        <sitemap priority=\"0.35\" change-frequency=\"monthly\"/>\n    </build-profile>\n\n    <footer>\n        <link href=\"https://github.com/PatilShreyas/bytemask\">View On GitHub</link>\n        <link href=\"https://github.com/PatilShreyas/bytemask/discussions\">Discuss</link>\n        <link href=\"https://github.com/PatilShreyas/bytemask/issues\">Create issue</link>\n        <link href=\"https://github.com/PatilShreyas/bytemask/blob/main/CONTRIBUTING.md\">Contribute</link>\n    </footer>\n</buildprofiles>\n"
  },
  {
    "path": "docs/Writerside/in.tree",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE instance-profile\n        SYSTEM \"https://resources.jetbrains.com/writerside/1.0/product-profile.dtd\">\n\n<instance-profile id=\"in\"\n                 name=\"Bytemask - Android Gradle Plugin\"\n                 start-page=\"Introduction.md\">\n\n    <toc-element topic=\"Introduction.md\"/>\n    <toc-element topic=\"Getting-Started.md\"/>\n    <toc-element topic=\"Configure.md\"/>\n    <toc-element topic=\"Declaring-properties.md\"/>\n    <toc-element topic=\"Read-configuration.md\"/>\n    <toc-element topic=\"Android-Customization.md\"/>\n    <toc-element toc-title=\"Releases\"\n                 target-for-accept-web-file-names=\"https://github.com/PatilShreyas/bytemask/releases\"/>\n</instance-profile>"
  },
  {
    "path": "docs/Writerside/redirection-rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE rules SYSTEM \"https://resources.jetbrains.com/writerside/1.0/redirection-rules.dtd\">\n<rules>\n    <!-- format is as follows\n    <rule id=\"<unique id>\">\n        <accepts>page.html</accepts>\n    </rule>\n    -->\n    <rule id=\"113781da\">\n        <description>Created after removal of \"Overview\" from Bytemask - Android Gradle Plugin</description>\n        <accepts>Overview.html</accepts>\n    </rule>\n    <rule id=\"6b2dcc15\">\n        <description>Created after removal of \"Tutorial\" from Bytemask - Android Gradle Plugin</description>\n        <accepts>Tutorial.html</accepts>\n    </rule>\n    <rule id=\"6b305a9\">\n        <description>Created after removal of \"Android app\" from Bytemask - Android Gradle Plugin</description>\n        <accepts>Android-app.html</accepts>\n    </rule>\n</rules>"
  },
  {
    "path": "docs/Writerside/topics/Android-Customization.md",
    "content": "# App Runtime Customization\n\n## Initialization\n\nBytemask automatically gets initialized via App startup library at the time of launch.\nSo there is no need to initialize it explicitly. But if you want to control the initialization, follow these steps:\n\n### 1. Disable the initializer\n\nIn the app's manifest, remove the initializer like this:\n\n```XML\n<provider\n    android:name=\"androidx.startup.InitializationProvider\"\n    android:authorities=\"${applicationId}.androidx-startup\"\n    android:exported=\"false\"\n    tools:node=\"merge\">\n\n    <meta-data\n        android:name=\"dev.shreyaspatil.bytemask.android.initializer.BytemaskInitializer\"\n        android:value=\"androidx.startup\"\n        tools:node=\"remove\" />\n</provider>\n```\n\n### 2. Initialize manually\n\n#### 2.1 Initializing with default config (App signing config)\n\n```Kotlin\nclass MyApp: Application() {\n    override fun onCreate() {\n        //...\n        AndroidBytemask.init(context = this)\n    }\n}\n```\n\n#### 2.2 Initializing with custom encryption specification\n\nIf you've provided [custom encryption specification](Configure.md \"Providing custom encryption key\") for the encryption, you can configure the same key \nfor decryption.\n\n```Kotlin\nclass CustomEncryptionKeyProvider : EncryptionKeyProvider {\n    override fun get(): Sha256DigestableKey() {\n        return Sha256DigestableKey(\"encryption_key_here\")\n    }\n}\n\nclass MyApp : Application() {\n    override fun onCreate() {\n        //...\n        Bytemask.init(CustomEncryptionKeyProvider())\n    }\n}\n```"
  },
  {
    "path": "docs/Writerside/topics/Configure.md",
    "content": "# Configure Plugin\n\nIn the module where plugin is applied, you can configure the plugin with following options:\n\n## Configuration option\n\n| Parameter                   | Description                                      | Default value       |\n|-----------------------------|--------------------------------------------------|---------------------|\n| `defaultPropertiesFileName` | The `.properties` file to read the strings from. | bytemask.properties |\n| `className`                 | Class name for the generated class               | BytemaskConfig      |\n\n### Variant configuration option\n\n| Parameter             | Description                                                                                                                                                                                                          | Default value                                                  |\n|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|\n| `enableEncryption`    | Whether to enable encryption for the current variant or not.                                                                                                                                                         | `false`                                                        |\n| `encryptionKeySource` | The source of encryption key. Possible sources: <br>**1. Signing Info:** Reads the signing info (SHA-256) directly from the signing config specified in the project <br>**2. Key**: Encrypts with the specified key. | -                                                              |\n| `encryptionSpec`      | The specification for encryption.                                                                                                                                                                                    | Algorithm = \"AES\" <br> Transformation = \"AES/CBC/PKCS5Padding\" |\n\n## Usage Examples\n\n### Configure custom file name and class name\n\n```Kotlin\nbytemaskConfig {\n    // Strings to read from\n    defaultPropertiesFileName.set(\"bytemask.properties\")\n    \n    // Class name for the generated class\n    className.set(\"BytemaskConfig\")\n}\n```\n\n### Configure for the \"release\" variant with signing config\n\n```Kotlin\nandroid {\n    signingConfigs { \n        create(\"release\") { \n            // Configure signing\n        } \n    }\n}\nbytemaskConfig {\n    // ...\n    configure(\"release\") {\n        enableEncryption.set(true)\n        \n        // This will pick signing info directly from the `android.signingConfigs`.\n        encryptionKeySource.set(KeySource.SigningConfig(name = \"release\"))\n    }\n}\n```\n\n### Provide a custom encryption key directly without depending on signing config info\n\n```Kotlin\nbytemaskConfig {\n    // ...\n    configure(\"release\") {\n        encryptionKeySource.set(KeySource.Key(\"encryption_key_here\"))\n    }\n}\n```\n\n### Providing encryption specification\n\n```Kotlin\nbytemaskConfig {\n    // ...\n    configure(\"release\") {\n        encryptionSpec.set(EncryptionSpec(algorithm = \"AES\", transformation = \"AES/GCM/NoPadding\"))\n    }\n}\n```\n\n### Configure for multiple flavours\n\n```Kotlin\nandroid {\n    flavorDimensions += \"version\"\n    productFlavors {\n        create(\"free\") { ... }\n        create(\"paid\") { ... }\n    }\n}\n\nbytemaskConfig {\n    configure(\"freeDebug\") {}\n    configure(\"freeRelease\") {}\n    configure(\"paidDebug\") {}\n    configure(\"paidRelease\") {}\n}\n```\n"
  },
  {
    "path": "docs/Writerside/topics/Declaring-properties.md",
    "content": "# Declaring properties\n\nBy default, plugin picks the properties from the `bytemask.properties` file.\nIf you've configured the different name\nfrom the [configuration](Configure.md \"Configure custom file name and class name\"), then you'll have to create a file\nwith that name.\n\nOnce configuration is done, Build the project 🔨 and class will be generated.\n\n## Example\n\n### Global configuration\n\nCreate `.properties` file in `/app` directory\n\n```Generic\nAPI_KEY=AI984013oindh48\nSERVER_SECRET=SUPERSECRET!!!\n```\n\n### Variant specific configuration\n\nCreate `.properties` file in variant source directory:\n\n- `/app/src/debug`\n\n```Generic\nAPI_KEY=DEBUG-AI984013oindh48\nSERVER_SECRET=SUPERSECRET!!!DEBUG\n```\n\n- `/app/src/release`\n\n```Generic\nAPI_KEY=PROD-AI984013oindh48\nSERVER_SECRET=SUPERSECRET!!!PROD\n```\n\n### Flavour specific configuration\n\nCreate `.properties` file in flavour source directory:\n\nAssuming we have `free` and `paid` variants, example:\n\n- `/app/src/freeDebug`\n\n```Generic\nAPI_KEY=DEBUG-AI984013oindh48\nSERVER_SECRET=SUPERSECRET!!!DEBUG\nADS_API_KEY=exaih8YJBhbhjBHJBhj\n```\n\n- `/app/src/paidRelease`\n\n```Generic\nAPI_KEY=PROD-AI984013oindh48\nSERVER_SECRET=SUPERSECRET!!!PROD\nPAYMENT_API_KEY=uguUGYGyg8G&IUG\n```\n\n> Properties will always be picked in the following order:\n> 1. Flavour\n> 2. Variant\n> 3. Global"
  },
  {
    "path": "docs/Writerside/topics/Getting-Started.md",
    "content": "# Getting Started\n\nThe Gradle plugin can be only applied to the following Android modules:\n\n- Application (`com.android.application`)\n- Dynamic Feature Module (`com.android.dynamic-feature`)\n\n> Library modules (`com.android.library`) can be used in more than one app, so libraries are not applicable for this \n> use case.\n{style=\"note\"}\n\n## Apply plugin\n\nApply the plugin in the app module.\n\n### Using Plugin DSL\n\n```Kotlin\nplugins {\n    id(\"dev.shreyaspatil.bytemask.plugin\") version \"1.0.0-beta01\"\n}\n```\n\n### OR using Legacy plugin application\n\n```Kotlin\nbuildscript {\n  repositories {\n    maven {\n      url = uri(\"https://plugins.gradle.org/m2/\")\n    }\n  }\n  dependencies {\n    classpath(\"dev.shreyaspatil.bytemask:gradle-plugin:1.0.0-beta01\")\n  }\n}\n\napply(plugin = \"dev.shreyaspatil.bytemask.plugin\")\n```\n"
  },
  {
    "path": "docs/Writerside/topics/Introduction.md",
    "content": "# Introduction\n\nBytemask is an Android Gradle Plugin that ***masks*** your secret strings for the app in the source code making it\ndifficult to extract from reverse engineering.\n\n## How does it work?\n\n### 1. Plugin - Encrypt strings at the compile time\nThe plugin is customizable. The default implementation of plugin encrypts strings with the public app signing info \n(SHA-256) and generated the config class with the encrypted value in the byte array format in the app's source.\n\n### 2. App - Decrypt strings in the runtime \n\nAt runtime, the app retrieves values from the configuration class. These values are decrypted using the SHA-256 hash \nof the app's signing certificate. This hash is fetched in the runtime using the `PackageManager` API in Android.\n\n> This security measure helps in the scenario of app tampering. If someone tries to reverse engineer the app and rebuild \n> it with their own code (_and with a different signing key_), the app will crash at runtime. This is because the \n> configuration keys are **encrypted using the app's original signing key**, and an **invalid key in the modified APK** \n> will be a cause to fail the decryption.\n\nSee the flow for better understanding:\n\n![Bytemask Flow.svg](Flow.svg)\n\n### How does it generate code?\n\nOnce you declare the secret properties in `.properties` file, it generates a class with the properties provided earlier\nby encrypting them and storing it in the form of bytes.\n\nSee image here (**Left:** Property declarations, **Right:** Generated class):\n\n![Configuration and Generated Code](ConfigAndGeneratedCode.png)\n\n### How does it look after reverse engineering on obfuscated APK?\n\nIf you build a release APK with R8 obfuscation and optimizations enabled, this is how code looks like which makes it\ndifficult to understand.\n\nSee the sample decompiled code (by [jadx](https://github.com/skylot/jadx))\n\n![Decompiled Code](DecompiledCode.png)\n\n### What happens if APK is modified by unauthorized party?\n\nIf an unauthorized developer modifies an app (APK) by decompiling and rebuilding it, they won't be able to use the \noriginal signing key. This means the modified app will have a different signature. \n\nSince Bytemask encrypted secrets using the app's unique SHA-256 key, any modified app trying to access these secrets \nwill fail (crash) because it won't have the correct key (original SHA-256) in the runtime.\n\nIt fails with `javax.crypto.BadPaddingException`\n\n![Crash Stacktrace](Crash.png)"
  },
  {
    "path": "docs/Writerside/topics/Read-configuration.md",
    "content": "# Read configuration\n\nBy default, plugin creates a class with name `BytemaskConfig`.\nIf you've configured the different name\nfrom the [configuration](Configure.md \"Configure custom file name and class name\"), then you'll have to use that class.\n\n## Example\n\nIf properties file is like:\n\n```Generic\nAPI_KEY=Hello1234\n```\n\nIn code, you can access it like\n\n<tabs>\n<code-block lang=\"kotlin\">\nfun example() {\n    val apiKey = BytemaskConfig.API_KEY\n}\n</code-block>\n<code-block lang=\"java\">\nvoid example() {\n    String apiKey = BytemaskConfig.apiKey()\n}\n</code-block>\n</tabs>\n\n![](ConfigAndGeneratedCode.png)"
  },
  {
    "path": "docs/Writerside/v.list",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE vars SYSTEM \"https://resources.jetbrains.com/writerside/1.0/vars.dtd\">\n<vars>\n    <var name=\"product\" value=\"Writerside\"/>\n</vars>\n"
  },
  {
    "path": "docs/Writerside/writerside.cfg",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ihp SYSTEM \"https://resources.jetbrains.com/writerside/1.0/ihp.dtd\">\n\n<ihp version=\"2.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n     xsi:noNamespaceSchemaLocation=\"https://resources.jetbrains.com/writerside/1.0/writerside-cfg.xsd\">\n    <topics dir=\"topics\"/>\n    <images dir=\"images\" web-path=\"images\"/>\n    <categories src=\"c.list\"/>\n    <vars src=\"v.list\"/>\n    <instance src=\"in.tree\"/>\n</ihp>"
  },
  {
    "path": "docs/webHelpIN2-all/HelpTOC.json",
    "content": "{\"entities\":{\"pages\":{\"how-to\":{\"id\":\"how-to\",\"title\":\"How to\",\"url\":\"how-to.html\",\"level\":0,\"tabIndex\":0}}},\"topLevelIds\":[\"how-to\"]}"
  },
  {
    "path": "docs/webHelpIN2-all/Map.jhm",
    "content": "<?xml version='1.0' encoding='UTF-8'?><map version=\"2.0\"><mapID target=\"how-to\" url=\"how-to.html\" default=\"yes\"/><mapID target=\"How to\" url=\"how-to.html\" default=\"no\"/><mapID target=\"how-to.md\" url=\"how-to.html\" default=\"yes\"/><mapID target=\"How+to\" url=\"how-to.html\" default=\"no\"/></map>"
  },
  {
    "path": "docs/webHelpIN2-all/api-object-digest.json",
    "content": "{}"
  },
  {
    "path": "docs/webHelpIN2-all/config.json",
    "content": "{\"productVersion\":\"\",\"productWebUrl\":\".\",\"productId\":\"in\",\"stage\":\"release\",\"downloadTitle\":\"Get Instance Name\",\"keymaps\":{},\"searchMaxHits\":75,\"productName\":\"Instance Name\"}"
  },
  {
    "path": "docs/webHelpIN2-all/current.help.version",
    "content": ""
  },
  {
    "path": "docs/webHelpIN2-all/how-to.html",
    "content": "<!DOCTYPE html SYSTEM \"about:legacy-compat\">\n<html lang=\"en-US\" data-preset=\"contrast\" data-primary-color=\"#307FFF\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><meta charset=\"UTF-8\"><meta name=\"robots\" content=\"noindex\"><meta name=\"built-on\" content=\"2024-06-01T09:39:48.7992198\"><title>How to | Instance Name</title><script type=\"application/json\" id=\"virtual-toc-data\">[{\"id\":\"before-you-start\",\"level\":0,\"title\":\"Before you start\",\"anchor\":\"#before-you-start\"},{\"id\":\"how-to-perform-a-task\",\"level\":0,\"title\":\"How to perform a task\",\"anchor\":\"#how-to-perform-a-task\"}]</script><script type=\"application/json\" id=\"topic-shortcuts\"></script><link href=\"https://resources.jetbrains.com/writerside/apidoc/6.10.0-b259/app.css\" rel=\"stylesheet\"><meta name=\"msapplication-TileColor\" content=\"#000000\"><link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"https://jetbrains.com/apple-touch-icon.png\"><link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"https://jetbrains.com/favicon-32x32.png\"><link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"https://jetbrains.com/favicon-16x16.png\"><meta name=\"msapplication-TileImage\" content=\"https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png\"><meta name=\"msapplication-square70x70logo\" content=\"https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png\"><meta name=\"msapplication-square150x150logo\" content=\"https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png\"><meta name=\"msapplication-wide310x150logo\" content=\"https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png\"><meta name=\"msapplication-square310x310logo\" content=\"https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png\"><meta name=\"image\" content=\"\"><!-- Open Graph --><meta property=\"og:title\" content=\"How to | Instance Name\"><meta property=\"og:description\" content=\"\"><meta property=\"og:image\" content=\"\"><meta property=\"og:site_name\" content=\"Instance Name Help\"><meta property=\"og:type\" content=\"website\"><meta property=\"og:locale\" content=\"en_US\"><meta property=\"og:url\" content=\"writerside-documentation/how-to.html\"><!-- End Open Graph --><!-- Twitter Card --><meta name=\"twitter:card\" content=\"summary_large_image\"><meta name=\"twitter:site\" content=\"\"><meta name=\"twitter:title\" content=\"How to | Instance Name\"><meta name=\"twitter:description\" content=\"\"><meta name=\"twitter:creator\" content=\"\"><meta name=\"twitter:image:src\" content=\"\"><!-- End Twitter Card --><!-- Schema.org WebPage --><script type=\"application/ld+json\">{\n    \"@context\": \"http://schema.org\",\n    \"@type\": \"WebPage\",\n    \"@id\": \"writerside-documentation/how-to.html#webpage\",\n    \"url\": \"writerside-documentation/how-to.html\",\n    \"name\": \"How to | Instance Name\",\n    \"description\": \"\",\n    \"image\": \"\",\n    \"inLanguage\":\"en-US\"\n}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type=\"application/ld+json\">{\n    \"@type\": \"WebSite\",\n    \"@id\": \"writerside-documentation/#website\",\n    \"url\": \"writerside-documentation/\",\n    \"name\": \"Instance Name Help\"\n}</script><!-- End Schema.org --></head><body data-id=\"how-to\" data-main-title=\"How to\" data-article-props=\"{&quot;seeAlsoStyle&quot;:&quot;links&quot;}\" data-template=\"article\" data-breadcrumbs=\"\"><div class=\"wrapper\"><main class=\"panel _main\"><header class=\"panel__header\"><div class=\"container\"><h3>Instance Name  Help</h3><div class=\"panel-trigger\"></div></div></header><section class=\"panel__content\"><div class=\"container\"><article class=\"article\" data-shortcut-switcher=\"inactive\"><h1 data-toc=\"how-to\" id=\"how-to.md\">How to</h1><p id=\"unao5z_53\">A How-to article is an action-oriented type of document. It explains how to perform a specific task or solve a problem, and usually contains a sequence of steps. Start with a short introductory paragraph that explains what users will accomplish by following this procedure, what they need to perform it for, or define the target audience of the doc.</p><aside class=\"prompt\" data-type=\"note\" data-title=\"\" id=\"unao5z_54\"><p id=\"unao5z_55\"><span class=\"control\" id=\"unao5z_56\">Highlight important information</span></p><p id=\"unao5z_57\">You can change the element to <span class=\"emphasis\" id=\"unao5z_58\">tip</span> or <span class=\"emphasis\" id=\"unao5z_59\">warning</span> by renaming the style attribute below.</p></aside><section class=\"chapter\"><h2 id=\"before-you-start\" data-toc=\"before-you-start\">Before you start</h2><p id=\"unao5z_60\">It is good practice to list the prerequisites that are required or recommended.</p><p id=\"unao5z_61\">Make sure that:</p><ul class=\"list _bullet\" id=\"unao5z_62\"><li class=\"list__item\" id=\"unao5z_63\"><p>First prerequisite</p></li><li class=\"list__item\" id=\"unao5z_64\"><p>Second prerequisite</p></li></ul></section><section class=\"chapter\"><h2 id=\"how-to-perform-a-task\" data-toc=\"how-to-perform-a-task\">How to perform a task</h2><p id=\"unao5z_65\">Some introductory information.</p><ol class=\"list _decimal\" id=\"unao5z_66\" type=\"1\"><li class=\"list__item\" id=\"unao5z_67\"><p id=\"unao5z_68\">Step with a code block</p><div class=\"code-block\" data-lang=\"bash\">\n run this --that\n</div></li><li class=\"list__item\" id=\"unao5z_70\"><p id=\"unao5z_71\">Step with a <a href=\"https://www.jetbrains.com\" id=\"unao5z_72\" data-external=\"true\" rel=\"noopener noreferrer\">link</a></p></li><li class=\"list__item\" id=\"unao5z_73\"><p id=\"unao5z_74\">Step with a list.</p><ul class=\"list _bullet\" id=\"unao5z_75\"><li class=\"list__item\" id=\"unao5z_76\"><p>List item</p></li><li class=\"list__item\" id=\"unao5z_77\"><p>List item</p></li><li class=\"list__item\" id=\"unao5z_78\"><p>List item</p></li></ul></li></ol></section><div class=\"last-modified\">Last modified: 01 June 2024</div><div data-feedback-placeholder=\"true\"></div><div class=\"navigation-links _bottom\"></div></article><div id=\"disqus_thread\"></div></div></section></main></div><script src=\"https://resources.jetbrains.com/writerside/apidoc/6.10.0-b259/app.js\"></script></body></html>"
  },
  {
    "path": "docs/webHelpIN2-all/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n<meta charset=\"utf-8\">\n<title>You will be redirected shortly</title>\n<meta http-equiv=\"refresh\" content=\"0; url=how-to.html\">\n<h1>Redirecting&hellip;</h1>\n<a href=\"how-to.html\">Click here if you are not redirected.</a>\n<script>location = \"how-to.html\"</script>\n</html>\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\nkotlin = \"1.9.24\"\nandroidGradlePlugin = \"8.3.2\"\nmavenPublish = \"0.28.0\"\ngradlePluginPublish = \"1.2.1\"\nappStartup = \"1.1.1\"\nannotationJvm = \"1.8.0\"\nspotless = \"6.25.0\"\njunit = \"4.13.2\"\nandroidx-test-ext-junit = \"1.1.5\"\nespresso-core = \"3.5.1\"\n\n[libraries]\nandroid-gradle-plugin = { module = \"com.android.tools.build:gradle\", version.ref = \"androidGradlePlugin\" }\nkotlin-gradle-plugin = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin\", version.ref = \"kotlin\" }\nappStartup = { module = \"androidx.startup:startup-runtime\", version.ref = \"appStartup\" }\nannotation-jvm = { group = \"androidx.annotation\", name = \"annotation-jvm\", version.ref = \"annotationJvm\" }\njunit = { group = \"junit\", name = \"junit\", version.ref = \"junit\" }\nandroidx-test-ext-junit = { group = \"androidx.test.ext\", name = \"junit\", version.ref = \"androidx-test-ext-junit\" }\nespresso-core = { group = \"androidx.test.espresso\", name = \"espresso-core\", version.ref = \"espresso-core\" }\n\n[plugins]\nspotless = { id = \"com.diffplug.spotless\", version.ref = \"spotless\" }\nmavenPublish = { id = \"com.vanniktech.maven.publish\", version.ref = \"mavenPublish\" }\ngradle-plugin-publish = { id = \"com.gradle.plugin-publish\", version.ref = \"gradlePluginPublish\" }\nkotlin-jvm = { id = \"org.jetbrains.kotlin.jvm\", version.ref = \"kotlin\" }\nandroid-app = { id = \"com.android.application\", version.ref = \"androidGradlePlugin\" }\nandroid-library = { id = \"com.android.library\", version.ref = \"androidGradlePlugin\" }\nkotlin-android = { id = \"org.jetbrains.kotlin.android\", version.ref = \"kotlin\" }"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Apr 21 14:01:36 IST 2024\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.4-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle-plugin/.gitignore",
    "content": "/build"
  },
  {
    "path": "gradle-plugin/build.gradle.kts",
    "content": "import com.android.build.gradle.internal.tasks.factory.dependsOn\n\nval GROUP: String by project\nval VERSION_NAME: String by project\nval POM_NAME: String by project\nval POM_DESCRIPTION: String by project\n\nplugins {\n    `kotlin-dsl`\n    `java-gradle-plugin`\n    alias(libs.plugins.gradle.plugin.publish)\n}\n\ngroup = GROUP\n\nversion = VERSION_NAME\n\ndependencies {\n    compileOnly(gradleApi())\n    compileOnly(kotlin(\"stdlib\"))\n    compileOnly(libs.kotlin.gradle.plugin)\n    compileOnly(libs.android.gradle.plugin)\n\n    implementation(project(\":bytemask-core\"))\n}\n\ntasks.getByName<Test>(\"test\") { useJUnitPlatform() }\n\ngradlePlugin {\n    website.set(\"https://github.com/PatilShreyas/bytemask\")\n    vcsUrl.set(\"https://github.com/PatilShreyas/bytemask.git\")\n    plugins {\n        create(\"reportGenPlugin\") {\n            id = \"dev.shreyaspatil.bytemask.plugin\"\n            displayName = POM_NAME\n            description = POM_DESCRIPTION\n            implementationClass = \"dev.shreyaspatil.bytemask.plugin.BytemaskPlugin\"\n            tags.set(listOf(\"android\", \"kotlin\", \"security\"))\n        }\n    }\n}\n\nval generateVersionClassTask =\n    tasks.register(\"generateVersionClass\") {\n        doLast {\n            val version =\n                project.findProperty(\"VERSION_NAME\")?.toString() ?: error(\"VERSION_NAME is not set\")\n            val file =\n                File(\n                    project.projectDir,\n                    \"build/generated/src/main/kotlin/dev/shreyaspatil/bytemask/plugin/Version.kt\"\n                )\n            file.parentFile.mkdirs()\n            file.writeText(\n                \"\"\"\n                package dev.shreyaspatil.bytemask.plugin\n                \n                object PluginConfig {\n                    const val VERSION = \"$version\"\n                }\n                \"\"\"\n                    .trimIndent()\n            )\n        }\n    }\n\ntasks.compileKotlin.dependsOn(generateVersionClassTask)\n\nsourceSets { main { java { srcDir(\"build/generated/src/main/kotlin\") } } }\n"
  },
  {
    "path": "gradle-plugin/gradle.properties",
    "content": "POM_ARTIFACT_ID=gradle-plugin\nPOM_NAME=Bytemask - Gradle Plugin\nPOM_DESCRIPTION=A gradle plugin to generates secured secrets for your application."
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/BytemaskPlugin.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin\n\nimport com.android.build.api.dsl.CommonExtension\nimport com.android.build.api.variant.ApplicationAndroidComponentsExtension\nimport com.android.build.api.variant.DynamicFeatureAndroidComponentsExtension\nimport com.android.build.api.variant.GeneratesApk\nimport com.android.build.api.variant.Variant\nimport dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec\nimport dev.shreyaspatil.bytemask.core.encryption.Sha256DigestableKey\nimport dev.shreyaspatil.bytemask.plugin.config.BytemaskConfig\nimport dev.shreyaspatil.bytemask.plugin.config.KeySource\nimport dev.shreyaspatil.bytemask.plugin.task.BytemaskCodegenTask\nimport dev.shreyaspatil.bytemask.plugin.util.capitalized\nimport java.io.File\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.provider.Provider\n\n/** Bytemask Gradle Plugin. Applies on Android projects. Entry point of the plugin. */\nclass BytemaskPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        var pluginApplied = false\n\n        val config = BytemaskConfig.create(target)\n\n        target.pluginManager.withPlugin(\"com.android.application\") {\n            pluginApplied = true\n            target.extensions.configure(ApplicationAndroidComponentsExtension::class.java) {\n                onVariants { variant -> handleVariant(variant, target, config) }\n            }\n        }\n\n        target.pluginManager.withPlugin(\"com.android.dynamic-feature\") {\n            pluginApplied = true\n            target.extensions.configure(DynamicFeatureAndroidComponentsExtension::class.java) {\n                onVariants { variant -> handleVariant(variant, target, config) }\n            }\n        }\n\n        target.afterEvaluate {\n            if (!pluginApplied) {\n                project.logger.error(\n                    \"The bytemask Gradle plugin needs to be applied on a project with either:\" +\n                        \"com.android.application, com.android.dynamic-feature, com.android.library\"\n                )\n            }\n        }\n\n        // Add the module dependencies\n        target.dependencies.add(\n            \"implementation\",\n            \"dev.shreyaspatil.bytemask:bytemask-android:${PluginConfig.VERSION}\"\n        )\n    }\n\n    private fun <T> handleVariant(variant: T, project: Project, config: BytemaskConfig) where\n    T : Variant,\n    T : GeneratesApk {\n        val codegenTask =\n            project.tasks.register(\n                \"bytemask${variant.name.capitalized()}\",\n                BytemaskCodegenTask::class.java\n            ) {\n                val propertyFileName = config.defaultPropertiesFileName.get()\n\n                bytemaskPropFiles.set(\n                    getBytemaskPropertyFiles(\n                        propertyFileName = propertyFileName,\n                        buildType = variant.buildType.orEmpty(),\n                        flavorNames = variant.productFlavors.map { it.second },\n                        root = project.projectDir\n                    )\n                )\n                applicationNamespace.set(variant.namespace)\n                className.set(config.className)\n\n                val task = this\n                val configForVariant = config.findConfigForVariant(variant.name)\n                if (configForVariant != null) {\n                    task.enableEncryption.set(configForVariant.enableEncryption.get())\n                    task.encryptionSpec.set(configForVariant.encryptionSpec.get())\n                    if (configForVariant.enableEncryption.get()) {\n                        val keySource = configForVariant.encryptionKeySource.orNull\n                        val encryptionKey =\n                            getEncryptionKey(\n                                keySource = keySource,\n                                project = project,\n                                variant = variant\n                            )\n                        task.encryptionKey.set(encryptionKey)\n                    }\n                } else {\n                    // Set default encryption spec\n                    task.enableEncryption.set(false)\n                    task.encryptionSpec.set(\n                        EncryptionSpec(algorithm = \"AES\", transformation = \"AES/CBC/PKCS5Padding\")\n                    )\n                }\n            }\n        variant.sources.java?.addGeneratedSourceDirectory(\n            taskProvider = codegenTask,\n            wiredWith = BytemaskCodegenTask::outputDirectory\n        )\n    }\n\n    private fun <T> getEncryptionKey(\n        keySource: KeySource?,\n        project: Project,\n        variant: T\n    ): Provider<Sha256DigestableKey?> where T : Variant =\n        project.provider {\n            when (keySource) {\n                is KeySource.SigningConfig ->\n                    getAppSigningKeyForVariant(\n                        project = project,\n                        keySource = keySource,\n                        variant = variant\n                    )\n                is KeySource.Key -> Sha256DigestableKey(value = keySource.encryptionKey)\n                null ->\n                    getAppSigningKeyForVariant(\n                        project = project,\n                        keySource = KeySource.SigningConfig(variant.name),\n                        variant = variant\n                    )\n            }\n        }\n\n    /** Returns the signing key for the variant. It will be used as an encryption key. */\n    private fun <T> getAppSigningKeyForVariant(\n        project: Project,\n        keySource: KeySource.SigningConfig,\n        variant: T\n    ): Sha256DigestableKey where T : Variant {\n        val signingConfig =\n            project.extensions\n                .getByType(CommonExtension::class.java)\n                .signingConfigs\n                .findByName(keySource.name)\n        return VariantSigningKeyProvider(signingConfig, variant.name).get()\n    }\n\n    companion object {\n\n        /** Returns property files for Bytemask. */\n        fun getBytemaskPropertyFiles(\n            propertyFileName: String,\n            buildType: String,\n            flavorNames: List<String>,\n            root: File\n        ): List<File> {\n            return getBytemaskPropFileLocations(propertyFileName, buildType, flavorNames).map {\n                root.resolve(it)\n            }\n        }\n\n        /**\n         * Returns possible locations of property file. It will be used to search for property file\n         * in the project.\n         */\n        fun getBytemaskPropFileLocations(\n            propertyFileName: String,\n            buildType: String,\n            flavorNames: List<String>\n        ): List<String> {\n            val fileLocations: MutableList<String> = ArrayList()\n            val flavorName =\n                flavorNames.stream().reduce(\"\") { a, b ->\n                    a + if (a.isEmpty()) b else b.capitalized()\n                }\n            fileLocations.add(\"\")\n            fileLocations.add(\"src/$flavorName/$buildType\")\n            fileLocations.add(\"src/$buildType/$flavorName\")\n            fileLocations.add(\"src/$flavorName\")\n            fileLocations.add(\"src/$buildType\")\n            fileLocations.add(\"src/\" + flavorName + buildType.capitalized())\n            fileLocations.add(\"src/$buildType\")\n            var fileLocation = \"src\"\n            for (flavor in flavorNames) {\n                fileLocation += \"/$flavor\"\n                fileLocations.add(fileLocation)\n                fileLocations.add(\"$fileLocation/$buildType\")\n                fileLocations.add(fileLocation + buildType.capitalized())\n            }\n            return fileLocations\n                .distinct()\n                .sortedBy { path -> path.count { it == '/' } }\n                .map { location: String ->\n                    if (location.isEmpty()) location + propertyFileName\n                    else \"$location/$propertyFileName\"\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/VariantSigningKeyProvider.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin\n\nimport com.android.build.api.dsl.ApkSigningConfig\nimport dev.shreyaspatil.bytemask.core.EncryptionKeyProvider\nimport dev.shreyaspatil.bytemask.core.encryption.Sha256DigestableKey\nimport java.security.KeyStore\nimport java.security.MessageDigest\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\n\n/** Provides [EncryptionKeyProvider] for a variant. */\nclass VariantSigningKeyProvider(\n    private val config: ApkSigningConfig?,\n    private val variantName: String,\n) : EncryptionKeyProvider {\n\n    override fun get(): Sha256DigestableKey {\n        checkNotNull(config) { errorMessage(\"Signing config not found for variant\") }\n        checkNotNull(config.storeFile) { errorMessage(\"Keystore file not found\") }\n        checkNotNull(config.storePassword) { errorMessage(\"Keystore password not found\") }\n        checkNotNull(config.keyAlias) { errorMessage(\"Key alias not found\") }\n        checkNotNull(config.keyPassword) { errorMessage(\"Key password not found\") }\n\n        val ks = KeyStore.getInstance(\"JKS\")\n        ks.load(config.storeFile!!.inputStream(), config.storePassword?.toCharArray())\n\n        val entry: KeyStore.PrivateKeyEntry =\n            ks.getEntry(\n                config.keyAlias,\n                KeyStore.PasswordProtection(config.keyPassword?.toCharArray())\n            ) as KeyStore.PrivateKeyEntry\n\n        val certificate = entry.certificate as X509Certificate\n        return Sha256DigestableKey(getFingerprint(certificate, \"SHA-256\"))\n    }\n\n    /** Returns the [Certificate] fingerprint as returned by `keytool`. */\n    private fun getFingerprint(cert: Certificate, hashAlgorithm: String): String {\n        val digest: MessageDigest = MessageDigest.getInstance(hashAlgorithm)\n        return formatSha256(digest.digest(cert.encoded))\n    }\n\n    /** Formats the SHA-256 [ByteArray] to a [String]. */\n    private fun formatSha256(value: ByteArray): String {\n        val sb = StringBuilder()\n        val len = value.size\n        for (i in 0 until len) {\n            val num = value[i].toInt() and 0xff\n            if (num < 0x10) {\n                sb.append('0')\n            }\n            sb.append(num.toString(16))\n        }\n        return sb.toString().uppercase()\n    }\n\n    private val errorSuffix =\n        \"Error occurred while configuring Bytemask for variant '$variantName':\"\n\n    private fun errorMessage(message: String): String {\n        return \"$errorSuffix $message\"\n    }\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/codegen/ConfigClassGenerator.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.codegen\n\nimport dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec\nimport dev.shreyaspatil.bytemask.core.encryption.Sha256DigestableKey\nimport dev.shreyaspatil.bytemask.core.encryption.asPlainValue\nimport dev.shreyaspatil.bytemask.core.encryption.encrypt\nimport dev.shreyaspatil.bytemask.plugin.util.camelCase\nimport dev.shreyaspatil.bytemask.plugin.util.normalize\nimport java.io.File\n\n/** Generates a Kotlin class file with properties and values. */\ninternal class ConfigClassGenerator(\n    private val applicationNamespace: String,\n    private val className: String,\n    private val propertyAndValuesProvider: PropertyAndValuesProvider,\n    private val outputDir: File,\n    private val encryptionDetail: EncryptionDetail?\n) {\n    fun generate(): File {\n        val className = className.normalize()\n        val declarationProperties = getDeclarationProperties()\n        val helperMethods = getHelperMethods()\n\n        return createClassKtFile(className).also {\n            it.writeText(\n                \"\"\"\n            |package $applicationNamespace\n            |\n            |object $className {\n            |$declarationProperties    \n            |$helperMethods\n            |}\n            \"\"\"\n                    .trimMargin()\n            )\n        }\n    }\n\n    /**\n     * Creates a Kotlin file with the given [className].\n     *\n     * @param className Name of the class.\n     */\n    private fun createClassKtFile(className: String) =\n        applicationNamespace\n            .replace(\".\", \"/\")\n            .let { packagePath -> outputDir.resolve(packagePath).also { it.mkdirs() } }\n            .resolve(\"$className.kt\")\n\n    private fun getHelperMethods() = buildString {\n        if (encryptionDetail == null) return@buildString\n\n        val (encryptionSpec, _) = encryptionDetail\n\n        // The reason for introducing separate function `buildString` here is because as a part of\n        // code optimization by R8, it converts bytes to String already in the code which exposes\n        // the string representation of the bytes because `String()` is an inline function in Kotlin\n        // SDK. So making a non-inline function here helps in preventing this exposure of encrypted\n        // strings.\n        appendLine(\n            \"\"\"\n            |   // Encryption Algorithm: ${encryptionSpec.algorithm}\n            |   private val _bytemaskEncryptionAlgorithm = ${encryptionSpec.algorithm.asByteArrayDeclaration()}\n            |   // Encryption Transformation: ${encryptionSpec.transformation}\n            |   private val _bytemaskEncryptionTransformation = ${encryptionSpec.transformation.asByteArrayDeclaration()}\n            |   \n            |   private val encryptionSpec by lazy { \n            |       dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec(\n            |           algorithm = buildString(_bytemaskEncryptionAlgorithm),\n            |           transformation = buildString(_bytemaskEncryptionTransformation)\n            |       )\n            |   }\n            |   \n            |   private fun unmask(bytes: ByteArray): Lazy<String> = lazy {\n            |       dev.shreyaspatil.bytemask.core.Bytemask.getInstance().unmask(encryptionSpec, buildString(bytes))\n            |   }\n            |   \n            |   private fun buildString(bytes: ByteArray): String = java.lang.String(bytes, Charsets.UTF_8) as String\n            \"\"\"\n                .trimMargin()\n        )\n    }\n\n    /**\n     * Generates a property declaration with encryption if [encryptionDetail] is not null.\n     * Otherwise, it generates a lazy property.\n     *\n     * ```kt\n     * // 1. With Encryption\n     * @JvmStatic\n     * @get:JvmName(\"property\")\n     * val property by unmask(byteArrayOf(1, 2, 3, 4, 5))\n     *\n     * // 2. Without Encryption\n     * @JvmStatic\n     * @get:JvmName(\"property\")\n     * val property by lazy { \"value\" }\n     * ```\n     */\n    private fun getDeclarationProperties() = buildString {\n        propertyAndValuesProvider.getAsMap().forEach { (property, value) ->\n            appendLine(\n                \"\"\"\n                        |\n                        |   /**\n                        |    * $value\n                        |    */\n                        |   @JvmStatic\n                        |   @get:JvmName(\"${property.camelCase()}\")\n                        |   ${propertyDeclaration(property, value)}\n                        \"\"\"\n                    .trimMargin()\n            )\n        }\n    }\n\n    /**\n     * Generates a property declaration with encryption if [encryptionDetail] is not null.\n     * Otherwise, it generates a lazy property.\n     *\n     * ```kt\n     * // 1. With Encryption\n     * val property by unmask(byteArrayOf(1, 2, 3, 4, 5))\n     *\n     * // 2. Without Encryption\n     * val property by lazy { \"value\" }\n     * ```\n     */\n    private fun propertyDeclaration(property: String, value: String): String {\n        return if (encryptionDetail != null) {\n            \"\"\"\n            |   val $property by unmask(${encryptAsBytes(value, encryptionDetail)})\n            \"\"\"\n                .trimIndent()\n        } else {\n            \"\"\"\n            |   val $property by lazy { \"${value.replace(\"$\", \"\\\\$\")}\" }\n            \"\"\"\n                .trimIndent()\n        }\n    }\n\n    /**\n     * Encrypts the [value] as bytes and returns it as a byte array declaration.\n     *\n     * ```kt\n     * byteArrayOf(1, 2, 3, 4, 5)\n     * ```\n     */\n    private fun encryptAsBytes(value: String, encryptionDetail: EncryptionDetail) =\n        value\n            .asPlainValue()\n            .encrypt(encryptionDetail.encryptionSpec, encryptionDetail.encryptionKey)\n            .value\n            .asByteArrayDeclaration()\n\n    /**\n     * Converts the string to a byte array declaration.\n     *\n     * ```kt\n     * byteArrayOf(1, 2, 3, 4, 5)\n     * ```\n     */\n    private fun String.asByteArrayDeclaration() =\n        \"byteArrayOf(${encodeToByteArray().joinToString(\", \")})\"\n\n    data class EncryptionDetail(\n        val encryptionSpec: EncryptionSpec,\n        val encryptionKey: Sha256DigestableKey\n    )\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/codegen/PropertyAndValuesProvider.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.codegen\n\nimport dev.shreyaspatil.bytemask.plugin.util.normalize\nimport java.io.File\nimport java.util.Properties\n\nclass PropertyAndValuesProvider(private val propFiles: List<File>) {\n    fun getAsMap(): Map<String, String> {\n        return propFiles\n            .map { Properties().apply { runCatching { load(it.inputStream()) } } }\n            .filter { it.isNotEmpty() }\n            .flatMap { it.entries }\n            .associate { it.key.toString().normalize() to it.value.toString() }\n    }\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/config/BytemaskConfig.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.config\n\nimport dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec\nimport dev.shreyaspatil.bytemask.plugin.config.impl.BytemaskConfigImpl\nimport org.gradle.api.Action\nimport org.gradle.api.Project\nimport org.gradle.api.provider.Property\n\n/** Configuration for Bytemask plugin. */\ninterface BytemaskConfig {\n    /** Default properties file name to retrieve properties and values from. */\n    val defaultPropertiesFileName: Property<String>\n\n    /** The class name of the generated class. */\n    val className: Property<String>\n\n    /** Configurations for each variant. */\n    val variantConfigs: Map<String, ByteMaskVariantConfig>\n\n    /** Configures Bytemask for a variant. */\n    fun configure(variant: String, config: Action<ByteMaskVariantConfig>)\n\n    /** Returns the configuration for the variant. */\n    fun findConfigForVariant(variant: String): ByteMaskVariantConfig?\n\n    companion object {\n        fun create(project: Project): BytemaskConfig =\n            project.extensions.create(\"bytemaskConfig\", BytemaskConfigImpl::class.java).apply {\n                defaultPropertiesFileName.convention(\"bytemask.properties\")\n                className.convention(\"BytemaskConfig\")\n            }\n    }\n}\n\n/** Configuration of Bytemask for variant. */\ninterface ByteMaskVariantConfig {\n    /** Enable encryption for this variant. */\n    val enableEncryption: Property<Boolean>\n\n    /** Encryption spec for this variant. */\n    val encryptionSpec: Property<EncryptionSpec>\n\n    /** Source of the key for encryption. */\n    val encryptionKeySource: Property<KeySource>\n}\n\n/** Represents the source of the key. */\nsealed class KeySource {\n    /**\n     * Represents the signing configuration.\n     *\n     * It means key will be retrieved from the declared signing configs for the module.\n     *\n     * @param name Name of the signing config.\n     */\n    class SigningConfig(val name: String) : KeySource()\n\n    /**\n     * Represents the key.\n     *\n     * It means key will [encryptionKey].\n     */\n    class Key(val encryptionKey: String) : KeySource()\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/config/impl/ByteMaskVariantConfigImpl.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.config.impl\n\nimport dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec\nimport dev.shreyaspatil.bytemask.plugin.config.ByteMaskVariantConfig\nimport dev.shreyaspatil.bytemask.plugin.config.KeySource\nimport javax.inject.Inject\nimport org.gradle.api.model.ObjectFactory\nimport org.gradle.api.provider.Property\nimport org.gradle.kotlin.dsl.property\n\ninternal abstract class ByteMaskVariantConfigImpl\n@Inject\nconstructor(objectFactory: ObjectFactory) : ByteMaskVariantConfig {\n\n    override val enableEncryption: Property<Boolean> =\n        objectFactory.property<Boolean>().convention(false)\n\n    override val encryptionSpec: Property<EncryptionSpec> =\n        objectFactory\n            .property<EncryptionSpec>()\n            .convention(EncryptionSpec(algorithm = \"AES\", transformation = \"AES/CBC/PKCS5Padding\"))\n\n    override val encryptionKeySource: Property<KeySource> = objectFactory.property<KeySource>()\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/config/impl/BytemaskConfigImpl.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.config.impl\n\nimport dev.shreyaspatil.bytemask.plugin.config.ByteMaskVariantConfig\nimport dev.shreyaspatil.bytemask.plugin.config.BytemaskConfig\nimport javax.inject.Inject\nimport org.gradle.api.Action\nimport org.gradle.api.model.ObjectFactory\n\ninternal abstract class BytemaskConfigImpl\n@Inject\nconstructor(private val objectFactory: ObjectFactory) : BytemaskConfig {\n    override val variantConfigs: MutableMap<String, ByteMaskVariantConfig> = mutableMapOf()\n\n    override fun configure(variant: String, config: Action<ByteMaskVariantConfig>) {\n        val variantConfig =\n            variantConfigs.getOrPut(variant) {\n                objectFactory.newInstance(ByteMaskVariantConfigImpl::class.java)\n            }\n        config.execute(variantConfig)\n    }\n\n    override fun findConfigForVariant(variant: String): ByteMaskVariantConfig? {\n        return variantConfigs[variant]\n    }\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/task/BytemaskCodegenTask.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.task\n\nimport dev.shreyaspatil.bytemask.core.encryption.EncryptionSpec\nimport dev.shreyaspatil.bytemask.core.encryption.Sha256DigestableKey\nimport dev.shreyaspatil.bytemask.plugin.codegen.ConfigClassGenerator\nimport dev.shreyaspatil.bytemask.plugin.codegen.PropertyAndValuesProvider\nimport java.io.File\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.GradleException\nimport org.gradle.api.file.DirectoryProperty\nimport org.gradle.api.provider.Property\nimport org.gradle.api.tasks.CacheableTask\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.InputFiles\nimport org.gradle.api.tasks.Optional\nimport org.gradle.api.tasks.OutputDirectory\nimport org.gradle.api.tasks.PathSensitive\nimport org.gradle.api.tasks.PathSensitivity\nimport org.gradle.api.tasks.TaskAction\n\n/** Task to generate Kotlin class file with properties and values. */\n@CacheableTask\nabstract class BytemaskCodegenTask : DefaultTask() {\n    /** Output directory to generate Kotlin class file. */\n    @get:OutputDirectory abstract val outputDirectory: DirectoryProperty\n\n    /** Properties files to read properties and values from. */\n    @get:PathSensitive(PathSensitivity.RELATIVE)\n    @get:InputFiles\n    abstract val bytemaskPropFiles: Property<Collection<File>>\n\n    /** Namespace of application / dynamic feature module. (e.g. com.example.app) */\n    @get:Input abstract val applicationNamespace: Property<String>\n\n    /** Class name of the generated class. (e.g. AppConfig). Default is `BytemaskConfig`. */\n    @get:Input abstract val className: Property<String>\n\n    /**\n     * Encryption key to encrypt properties. Required if [enableEncryption] is true. Provide the\n     * SHA-256 signature of the key by which app will be signed.\n     */\n    @get:Input @get:Optional abstract val encryptionKey: Property<Sha256DigestableKey>\n\n    /**\n     * Enable encryption for properties. Default is `false`. If enabled, [encryptionKey] is\n     * required.\n     */\n    @get:Input abstract val enableEncryption: Property<Boolean>\n\n    /** Encryption spec for encryption. Default is AES/CBC/PKCS5Padding. */\n    @get:Input abstract val encryptionSpec: Property<EncryptionSpec>\n\n    @Throws(GradleException::class)\n    @TaskAction\n    fun action() {\n        val propFiles = bytemaskPropFiles.get().filter { it.isFile }\n\n        if (propFiles.isEmpty()) {\n            val message =\n                \"\"\"\n                The Bytemask Plugin cannot function without `.properties` file. \n                Searched locations: ${\n                    bytemaskPropFiles.get().joinToString { it.absolutePath }\n                }\n                \"\"\"\n                    .trimIndent()\n\n            logger.error(message)\n            return\n        }\n\n        // Delete all existing content of outputdir.\n        val outputDir = outputDirectory.get().asFile\n        outputDir.deleteRecursively()\n\n        if (!outputDir.mkdirs()) {\n            throw GradleException(\"Failed to create folder: $outputDir\")\n        }\n\n        val encryptionDetail = getEncryptionDetail()\n\n        // Generate code\n        val codegen =\n            ConfigClassGenerator(\n                propertyAndValuesProvider = PropertyAndValuesProvider(propFiles),\n                applicationNamespace = applicationNamespace.get(),\n                className = className.get(),\n                outputDir = outputDir,\n                encryptionDetail = encryptionDetail\n            )\n\n        val generatedFile = codegen.generate()\n\n        logger.info(\"Generated Bytemask config file: ${generatedFile.absolutePath}\")\n    }\n\n    private fun getEncryptionDetail(): ConfigClassGenerator.EncryptionDetail? =\n        if (enableEncryption.get()) {\n            if (encryptionKey.orNull == null) {\n                throw GradleException(\"Encryption key is required for encryption.\")\n            }\n\n            ConfigClassGenerator.EncryptionDetail(\n                encryptionSpec = encryptionSpec.get(),\n                encryptionKey = encryptionKey.get()\n            )\n        } else {\n            null\n        }\n}\n"
  },
  {
    "path": "gradle-plugin/src/main/java/dev/shreyaspatil/bytemask/plugin/util/StringExt.kt",
    "content": "/**\n * Copyright 2024 Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dev.shreyaspatil.bytemask.plugin.util\n\n/** Capitalizes the first character of the string. Example: \"hello\" -> \"Hello\" */\nfun String.capitalized(): String {\n    return this.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }\n}\n\n/**\n * Converts the string to camelCase. Example:\n * 1. \"hello world\" -> \"helloWorld\"\n * 2. \"hello_world\" -> \"helloWorld\"\n * 3. \"helloWorld\" -> \"helloWorld\"\n * 4. \"HelloWorld\" -> \"helloWorld\"\n */\nfun String.camelCase(): String {\n    return this.split(\" \", \"_\")\n        .mapIndexed { index, word ->\n            if (index == 0) word.lowercase() else word.lowercase().capitalized()\n        }\n        .joinToString(\"\")\n}\n\n/**\n * Cleans the string by replacing special characters with underscore.\n *\n * Example:\n * 1. \"Hello!World\" -> \"Hello_World\"\n * 2. \"Hey-There\" -> \"Hey_There\"\n */\nfun String.normalize(): String {\n    return replace(\"[^A-Za-z0-9 _]\".toRegex(), \"_\")\n}\n\n/**\n * Removes special characters from the string.\n *\n * Example:\n * 1. \"Hello!World\" -> \"HelloWorld\"\n * 2. \"Hey-There\" -> \"HeyThere\"\n */\nfun String.removeSpecialChars(): String {\n    return replace(\"[^A-Za-z0-9 ]\".toRegex(), \"\")\n}\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\nandroid.useAndroidX=true\nkotlin.code.style=official\nandroid.nonTransitiveRClass=true\n\n# Lbrary configuration\nGROUP=dev.shreyaspatil.bytemask\nVERSION_NAME=1.0.0-beta01\n\nSONATYPE_HOST=DEFAULT\nRELEASE_SIGNING_ENABLED=true\n\nPOM_INCEPTION_YEAR=2023\nPOM_URL=https://github.com/PatilShreyas/bytemask/\n\nPOM_LICENSE_NAME=MIT\nPOM_LICENSE_URL=https://raw.githubusercontent.com/PatilShreyas/bytemask/main/LICENSE\nPOM_LICENSE_DIST=repo\n\nPOM_SCM_URL=https://github.com/PatilShreyas/bytemask/\nPOM_SCM_CONNECTION=scm:git:git://github.com/PatilShreyas/bytemask.git\nPOM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/PatilShreyas/bytemask.git\n\nPOM_DEVELOPER_ID=PatilShreyas\nPOM_DEVELOPER_NAME=Shreyas Patil\nPOM_DEVELOPER_URL=https://github.com/PatilShreyas/"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "pluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n        maven(url = \"https://plugins.gradle.org/m2/\")\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        mavenLocal()\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.name = \"ByteMask\"\ninclude(\":app\")\ninclude(\":bytemask-core\")\ninclude(\":bytemask-android\")\ninclude(\":gradle-plugin\")"
  },
  {
    "path": "spotless/copyright.kt",
    "content": "/**\n * Copyright $YEAR Shreyas Patil\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */"
  }
]