[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: jacopotediosi\ncustom: https://paypal.me/jacopotediosi\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug Report\nabout: Report something that isn't working\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n## Overview\n[NOTE]: # ( Give a BRIEF summary about your problem )\n\n\n## Steps to Reproduce\n[NOTE]: # ( Provide a simple set of steps to reproduce this bug. )\n1.  \n2.  \n3.  \n\n## Expected Behavior\n[NOTE]: # ( Tell us what you expected to happen )\n\n\n## Actual Behavior\n[NOTE]: # ( Tell us what actually happens )\n\n\n## Screenshots\n[NOTE]: # ( If applicable, add screenshots to help explain your problem. )\n\n\n## System Information\n- Device and model: \n\n- ROM and Android version: \n\n- Is the Google app you are trying to tweak (e.g., Phone by Google) installed as system app: yes/no\n\n- Installed Magisk / other SU Manager version: \n\n[NOTE]: # ( Paste below the output of the `adb shell \"dumpsys package com.jacopomii.gappsmod | grep version\"` command )\n- Installed GAppsMod version:\n\n\n[NOTE]: # ( Paste below the output of the `adb shell \"dumpsys package REPLACE_WITH_PACKAGENAME | grep version\"` command )\n- The version of the Google app you are trying to tweak (e.g., Phone by Google):\n\n\n[NOTE]: # ( Paste below the output of the `adb shell \"getprop | grep locale\"` command )\n- Your device language (locale):\n\n\n[NOTE]: # ( Paste below the output of the `adb shell \"getprop | grep iso-country\"` command )\n- Your location (country of the SIM and country where you are):\n\n\n## Logcat\n[NOTE]: # ( \nLaunch the Google app you are trying to tweak (e.g., Phone by Google) in Debug mode using the `adb shell \"am start -D REPLACE_WITH_PACKAGENAME\"` command.\nOpen another terminal and use the `adb logcat > logs.txt` command to start capturing logs.\nPerform the necessary steps to replicate the bug, then press CTRL+C to stop capturing logs.\nAttach below the resulting logs.txt file.\n)\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature Request\nabout: Suggest an idea for a new feature you want\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n## Summary\n[NOTE]: # ( Provide a brief overview of what the new feature is all about )\n\n\n## Solution\n[NOTE]: # ( A clear and concise description of what you want to happen )\n\n\n## Examples\n[NOTE]: # ( Show us a picture or mock-up of your proposal )\n\n\n## Alternatives\n[NOTE]: # ( A clear and concise description of any alternative solutions or features you've considered )\n\n\n## Context\n[NOTE]: # ( Why does this feature matter to you? What unique circumstances do you have? )\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gradle\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/push.yml",
    "content": "name: Assemble on push\n\non:\n  push:\n    branches: [ main ]\n    paths-ignore:\n      - '.github/**'\n      - '**.md'\n  workflow_dispatch:\n\njobs:\n  build:\n    name: Build on ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ ubuntu-latest ]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n        with:\n          submodules: 'recursive'\n          fetch-depth: 0\n\n      - name: Gradle wrapper validation\n        uses: gradle/wrapper-validation-action@v1\n\n      - name: Set up JDK 17\n        uses: actions/setup-java@v3\n        with:\n          distribution: 'temurin'\n          java-version: '17'\n\n      - name: Write keystore parameters\n        run: |\n          echo keystore.password='${{ secrets.KEYSTORE_PASSWORD }}' >> local.properties\n          echo keystore.alias='${{ secrets.KEYSTORE_ALIAS }}' >> local.properties\n          echo keystore.alias_password='${{ secrets.KEYSTORE_ALIAS_PASSWORD }}' >> local.properties\n          echo keystore.path=`pwd`/keystore.jks >> local.properties\n          echo \"${{ secrets.KEYSTORE_KEY }}\" | base64 --decode > keystore.jks\n\n      - name: Gradle Dependency Submission\n        uses: mikepenz/gradle-dependency-submission@v0.8.6\n        with:\n          gradle-build-module: \":app\"\n\n      - name: Assemble\n        uses: gradle/gradle-build-action@v2\n        with:\n          arguments: assemble\n\n      - name: Get short commit hash\n        run: |\n          echo \"LATEST_COMMIT_HASH=$(git rev-parse --short HEAD)\" >> $GITHUB_ENV\n\n      - name: Upload debug\n        if: success()\n        uses: actions/upload-artifact@v3\n        with:\n          name: ${{ github.event.repository.name }}-${{ env.LATEST_COMMIT_HASH }}-debug.apk\n          path: \"app/build/outputs/apk/debug/app-debug.apk\"\n\n      - name: Upload release\n        if: success()\n        uses: actions/upload-artifact@v3\n        with:\n          name: ${{ github.event.repository.name }}-${{ env.LATEST_COMMIT_HASH }}-release.apk\n          path: \"app/build/outputs/apk/release/app-release.apk\"\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n.idea\n.DS_Store\n/build\nlocal.properties\n"
  },
  {
    "path": "README.md",
    "content": "# Deprecation notice\n## !!! This project is no longer maintained !!!\n\nI had started researching how Phenotype DB works as a hobby, and this application was only meant to be a proof of concept.\n\nMaking things work required a huge amount of reverse engineering of Google applications, and today I no longer have time to dedicate to it.\n\nI've also been told that the structure of Phenotype DB has been recently changed, so I expect this project to no longer work, or to stop working soon.\n\nIf you are looking for a replacement, I suggest you take a look at [GMS-Flags](https://github.com/polodarb/GMS-Flags), a still maintained application created by other users who were part of the community of this project itself.\n\n# GAppsMod (ex GoogleDialerMod)\nThe ultimate All-In-One Utility to tweak Google applications.\n\n\n## Downloads:\n - Please visit the [GAppsMod Release Page](https://github.com/jacopotediosi/GAppsMod/releases)\n\n\n## How do I use it?\n- Always make sure you're using the latest beta version of the Google apps you want to tweak to take advantage of the latest features\n- Allow root access to GAppsMod, apply any mods you want, then force close and reopen Google apps a few times for them to take effect\n- There is no need to keep GAppsMod installed after applying the desired mods, because they (should) survive Google applications updates / reinstalls over time\n\n\n## How does it work?\nIn every Android device there is a database, called Phenotype.db, managed by Google Play Services, containing \"flags\" that affect the behavior of all installed Google applications.\n\nSome of those flags concern applications core functionalities, while others pertain to hidden or upcoming features that have not yet been released.\n\nWhat GAppsMod does is execute SQLite queries on that database and override the configuration files of Google applications to enable or modify their functionality at will.\n\n\n## Features:\n- Supports all arm / arm64 / x86 / x86_64 devices and all Android versions from 5.0 (Lollipop)\n- Enable / disable hidden features for all users at once when Android \"multiple users\" mode is in use\n- Allows users to list and change all Phenotype DB boolean flags for all installed Google applications\n- A convenient home screen brings together the suggested mods for the most used Google applications\n\n\n## Currently suggested mods\n- For the **Phone** application ([link](https://play.google.com/store/apps/details?id=com.google.android.dialer)):\n    - Force **enable call recording** feature, even on unsupported devices or in unsupported countries ([ref](https://support.google.com/phoneapp/answer/9803950))\n        - Enable also **automatic call recording** (\"always record\") feature based on caller (otherwise only available in India)\n    - Silence the annoying \"registration has started / ended\" **call recording announcements** (only on Phone version <= 94.x)\n    - Force **enable call screening** and \"revelio\" (advanced automatic call screening) features, even on unsupported devices or in unsupported countries ([ref](https://support.google.com/phoneapp/answer/9118387))\n        - Allows users to choose the language for call screening\n- For the **Messages** application ([link](https://play.google.com/store/apps/details?id=com.google.android.apps.messaging)):\n    - Force **enable debug menu** (it can also be enabled without mods by entering `*xyzzy*` in the application's search field)\n    - Force **enable message organization** (\"supersort\")\n    - Force **enable marking conversations as unread**\n    - Force **enable verified SMS** settings menu ([ref](https://support.google.com/messages/answer/9326240))\n    - Force **enable always sending images by Google Photos links in SMS** ([ref](https://9to5google.com/2022/02/19/messages-google-photos/))\n    - Force **enable nudges and birthday reminders** ([ref](https://support.google.com/messages/answer/11555591))\n    - Force **enable Bard AI draft suggestions** (\"magic compose\") ([ref](https://9to5google.com/2023/05/05/google-messages-magic-compose-ai/))\n    - Force enable smart features: **spotlights suggestions** ([ref](https://9to5google.com/2023/02/02/google-messages-assistant/)), **stickers suggestions**, **smart compose** ([ref](https://9to5google.com/2020/06/30/gboard-android-smart-compose-google-messages/)), **smart actions (smart reply) in notifications**\n\nAnd much more coming soon :)\n\n\n## Demo\n![Demo GIF](https://github.com/jacopotediosi/GAppsMod/assets/20026524/5b13c935-4b12-46ac-b67d-0182004c8ac0)\n\n\n## Troubleshooting:\n- After enabling / disabling any mod, please force close and reopen a few times the Google application you are trying to mod. You may also need to reboot for the changes to take effect.\n- Before to report an issue try to delete Google apps data, to reboot your phone and to try again what didn't work\n\n\n## Donations\nIf you really like my work, please consider a donation via [Paypal](https://paypal.me/jacopotediosi) or [Github Sponsor](https://github.com/sponsors/jacopotediosi). Even a small amount will be appreciated.\n\n\n## Credits:\n- Thanks to [Gabriele Rizzo aka shmykelsa](https://github.com/shmykelsa), [Jen94](https://github.com/jen94) and [SAAX by agentdr8](https://gitlab.com/agentdr8/saax) for their [AA-Tweaker](https://github.com/shmykelsa/AA-Tweaker) app, which inspired me making GAppsMod\n- [Libsu](https://github.com/topjohnwu/libsu) by [topjohnwu](https://github.com/topjohnwu)\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build"
  },
  {
    "path": "app/build.gradle",
    "content": "plugins {\n    id \"com.android.application\"\n    id \"com.google.protobuf\"\n    id \"com.likethesalad.stem\"\n}\n\ndef keyfile\ndef keystorePSW\ndef keystoreAlias\ndef keystoreAliasPSW\n\nProperties properties = new Properties()\nproperties.load(project.rootProject.file(\"local.properties\").newDataInputStream())\ndef keystoreFilepath = properties.getProperty(\"keystore.path\")\n\nif (keystoreFilepath) {\n    keyfile = file(keystoreFilepath)\n    keystorePSW = properties.getProperty(\"keystore.password\")\n    keystoreAlias = properties.getProperty(\"keystore.alias\")\n    keystoreAliasPSW = properties.getProperty(\"keystore.alias_password\")\n} else {\n    // Remember to config your keystore settings in local.properties or in the below lines\n    keyfile = file(\"C:/keystore.jks\")\n    keystorePSW = \"CHANGEME\"\n    keystoreAlias = \"CHANGEME\"\n    keystoreAliasPSW = \"CHANGEME\"\n}\n\nandroid {\n    namespace \"com.jacopomii.gappsmod\"\n    compileSdk 33\n\n    defaultConfig {\n        applicationId \"com.jacopomii.gappsmod\"\n        minSdk 21\n        targetSdk 33\n        versionCode 400\n        versionName \"4.00\"\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    signingConfigs {\n        release {\n            storeFile keyfile\n            storePassword keystorePSW\n            keyAlias keystoreAlias\n            keyPassword keystoreAliasPSW\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled true\n            shrinkResources true\n            proguardFiles getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\"\n            signingConfig signingConfigs.release\n        }\n    }\n\n    buildFeatures {\n        viewBinding true\n        aidl true\n        buildConfig true\n    }\n\n    sourceSets {\n        main {\n            res.srcDirs = [\"src/main/res\",\n                           \"src/main/res/layouts/activities\",\n                           \"src/main/res/layouts/dialogs\",\n                           \"src/main/res/layouts/fragments\",\n                           \"src/main/res/layouts/items\"]\n        }\n    }\n}\n\ndependencies {\n    // Libsu\n    def libsuVersion = \"5.0.5\"\n    implementation \"com.github.topjohnwu.libsu:core:${libsuVersion}\"\n    implementation \"com.github.topjohnwu.libsu:service:${libsuVersion}\"\n    implementation \"com.github.topjohnwu.libsu:nio:${libsuVersion}\"\n\n    // Official SQLite Java Bindings, downloaded from https://www.sqlite.org/download.html\n    implementation files(\"libs/sqlite-android-3410200.aar\")\n\n    // Advanced toast\n    implementation \"com.github.GrenderG:Toasty:1.5.2\"\n\n    // HTTP\n    implementation \"com.android.volley:volley:1.2.1\"\n\n    // Protobuf\n    implementation \"com.google.protobuf:protobuf-javalite:3.23.1\"\n\n    // Apache Commons\n    //noinspection GradleDependency\n    implementation \"commons-io:commons-io:2.12.0\"\n    implementation \"org.apache.commons:commons-lang3:3.12.0\"\n\n    // Navigation drawer\n    def navigationVersion = \"2.5.3\"\n    implementation \"androidx.navigation:navigation-fragment:${navigationVersion}\"\n    implementation \"androidx.navigation:navigation-ui:${navigationVersion}\"\n\n    // FastScroller for RecyclerView\n    implementation \"io.github.l4digital:fastscroll:2.1.0\"\n\n    // Other UI Components\n    implementation \"com.google.android.material:material:1.9.0\"\n    implementation \"androidx.appcompat:appcompat:1.6.1\"\n    implementation \"androidx.constraintlayout:constraintlayout:2.1.4\"\n}\n\nprotobuf {\n    protoc {\n        artifact = \"com.google.protobuf:protoc:3.23.1\"\n    }\n    generateProtoTasks {\n        all().each { task ->\n            task.builtins {\n                java {\n                    option \"lite\"\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n    <issue id=\"UnsafeNativeCodeLocation\">\n        <ignore path=\"src/main/res/raw/sqlite3_arm\" />\n        <ignore path=\"src/main/res/raw/sqlite3_x86\" />\n    </issue>\n</lint>"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n\n# Keep protobuf autogenerated classes\n-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }\n\n# Keep SQLite Java Bindings classes\n-keep class org.sqlite.** { *; }\n-keep class org.sqlite.database.** { *; }"
  },
  {
    "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    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n\n    <application\n        android:name=\".application.GAppsModApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\"\n        tools:ignore=\"DataExtractionRules\"\n        tools:replace=\"android:allowBackup\">\n\n        <activity\n            android:name=\".ui.activity.SplashScreenActivity\"\n            android:exported=\"true\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/AppTheme\"\n            tools:ignore=\"LockedOrientationActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".ui.activity.MainActivity\"\n            android:configChanges=\"keyboard|keyboardHidden|orientation|screenLayout|screenSize\"\n            android:exported=\"true\"\n            android:theme=\"@style/AppTheme\"\n            android:windowSoftInputMode=\"adjustResize\" />\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/aidl/com/jacopomii/gappsmod/ICoreRootService.aidl",
    "content": "package com.jacopomii.gappsmod;\n\ninterface ICoreRootService {\n    IBinder getFileSystemService();\n\n    /**\n    * Query the GMS and Vending Phenotype DBs to get a list of all package names that have at least\n    * one Flag set.\n    *\n    * @return a {@code HashMap} in the format \"Phenotype package name\" => \"Android package name\".\n    */\n    Map phenotypeDBGetAllPackageNames();\n\n    /**\n    * Query the GMS and Vending Phenotype DBs to get a list of all package names that have at least\n    * one Flag overridden.\n    *\n    * @return a {@code HashMap} in the format \"Phenotype package name\" => \"Android package name\".\n    */\n    Map phenotypeDBGetAllOverriddenPackageNames();\n\n    /**\n     * Query the GMS/Vending Phenotype DB (based on the {@code phenotypePackageName}) to get the\n     * Android package name corresponding to a given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype package name for which the corresponding Android\n     * package name is to be returned.\n     * @return the Android package name corresponding to the specified {@code phenotypePackageName}.\n     * An empty string if the phenotypePackageName couldn't be found.\n    */\n    String phenotypeDBGetAndroidPackageNameByPhenotypePackageName(in String phenotypePackageName);\n\n    /**\n     * Query the GMS/Vending Phenotype DB (based on the {@code phenotypePackageName}), selecting all\n     * the boolean flags for a given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name whose flags are to be\n     * returned.\n     * @return a {@code HashMap} that uses the ({@code String}) flag name as the key.<br>\n     * For performance reasons, the value of this HashMap is a {@code List} structured as follows:<br>\n     * - Position 0 contains the {@code Boolean} value of the flag, giving priority to the value\n     * overridden in the FlagOverrides table, if present, over the one contained in the Flags table.<br>\n     * - Position 1 contains the {@code Boolean} value \"changed\", which is {@code true} if and only\n     * if the returned flag is overwritten in the FlagOverrides table and has a different value\n     * than the one contained in the Flags table; {@code false} otherwise.\n     */\n    Map phenotypeDBGetBooleanFlagsOrOverridden(in String phenotypePackageName);\n\n    /**\n     * Query the GMS/Vending Phenotype DB (based on the {@code phenotypePackageName}) to find out if\n     * all given {@code flags} are overridden for a given {@code phenotypePackageName}.\n     * Please note that the fact that a flag is overridden only implies that it is present in the\n     * FlagOverrides table, and not that its value is different from what is indicated in the Flags\n     * table.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to check.\n     * @param flags the flags to check.\n     * @return {@code true} if all given {@code flags} are overridden for the given\n     * {@code phenotypePackageName}; {@code false} otherwise.\n     */\n    boolean phenotypeDBAreAllFlagsOverridden(in String phenotypePackageName, in List<String> flags);\n\n    /**\n     * Remove all flag overrides from the GMS and Vending Phenotype DBs by truncating the\n     * FlagOverrides table.\n     * It also clears from the filesystem the Phenotype cache of all applications for which at least\n     * one flag was overridden.\n     */\n    void phenotypeDBDeleteAllFlagOverrides();\n\n    /**\n     * Delete all flag overrides from the GMS/Vending Phenotype DB (based on the\n     * {@code phenotypePackageName}) for a given {@code phenotypePackageName}.\n     * It also clears from the filesystem the Phenotype cache of the application corresponding to\n     * the given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to delete the\n     * flag overrides.\n     */\n    void phenotypeDBDeleteAllFlagOverridesByPhenotypePackageName(in String phenotypePackageName);\n\n    /**\n     * Delete a given list of flag overrides from the GMS/Vending Phenotype DB (based on the\n     * {@code phenotypePackageName}) for a given {@code phenotypePackageName}.\n     * It also clears from the filesystem the Phenotype cache of the application corresponding to\n     * the given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to delete the\n     * flag overrides.\n     * @param flags the list of flags to delete.\n     */\n    void phenotypeDBDeleteFlagOverrides(in String phenotypePackageName, in List<String> flags);\n\n    /**\n     * Override the value of a boolean flag in the GMS/Vending Phenotype DB (based on the\n     * {@code phenotypePackageName}) for a given {@code phenotypePackageName}.\n     * It also clears from the filesystem the Phenotype cache of the application corresponding to\n     * the given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to override\n     * the flag.\n     * @param flag the name of the flag to override.\n     * @param value the value to override the flag with.\n     */\n    void phenotypeDBOverrideBooleanFlag(in String phenotypePackageName, in String flag, in boolean value);\n\n    /**\n     * Override the value of an extension flag in the GMS/Vending Phenotype DB (based on the\n     * {@code phenotypePackageName}) for a given {@code phenotypePackageName}.\n     * It also clears from the filesystem the Phenotype cache of the application corresponding to\n     * the given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to override\n     * the flag.\n     * @param flag the name of the flag to override.\n     * @param value the value to override the flag with.\n     */\n    void phenotypeDBOverrideExtensionFlag(in String phenotypePackageName, in String flag, in byte[] value);\n\n    /**\n     * Override the value of a string flag in the GMS/Vending Phenotype DB (based on the\n     * {@code phenotypePackageName}) for a given {@code phenotypePackageName}.\n     * It also clears from the filesystem the Phenotype cache of the application corresponding to\n     * the given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to override\n     * the flag.\n     * @param flag the name of the flag to override.\n     * @param value the value to override the flag with.\n     */\n    void phenotypeDBOverrideStringFlag(in String phenotypePackageName, in String flag, in String value);\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/application/GAppsModApplication.java",
    "content": "package com.jacopomii.gappsmod.application;\n\nimport android.app.Application;\n\nimport com.google.android.material.color.DynamicColors;\n\npublic class GAppsModApplication extends Application {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        DynamicColors.applyToActivitiesIfAvailable(this);\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/data/BooleanFlag.java",
    "content": "package com.jacopomii.gappsmod.data;\n\npublic class BooleanFlag {\n    private final String mFlagName;\n    private boolean mFlagValue;\n    private boolean mFlagOverriddenAndChanged;\n\n    public BooleanFlag(String flagName, boolean flagValue, boolean flagOverriddenAndChanged) {\n        mFlagName = flagName;\n        mFlagValue = flagValue;\n        mFlagOverriddenAndChanged = flagOverriddenAndChanged;\n    }\n\n    public String getFlagName() {\n        return mFlagName;\n    }\n\n    public boolean getFlagValue() {\n        return mFlagValue;\n    }\n\n    public void setFlagValue(boolean flagValue) {\n        mFlagValue = flagValue;\n    }\n\n    public void setFlagOverriddenAndChanged(boolean flagOverriddenAndChanged) {\n        mFlagOverriddenAndChanged = flagOverriddenAndChanged;\n    }\n\n    public boolean getFlagOverriddenAndChanged() {\n        return mFlagOverriddenAndChanged;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/data/Constants.java",
    "content": "package com.jacopomii.gappsmod.data;\n\nimport android.annotation.SuppressLint;\n\n@SuppressLint(\"SdCardPath\")\npublic interface Constants {\n    // Android package names\n    String GMS_ANDROID_PACKAGE_NAME = \"com.google.android.gms\";\n    String VENDING_ANDROID_PACKAGE_NAME = \"com.android.vending\";\n    String DIALER_ANDROID_PACKAGE_NAME = \"com.google.android.dialer\";\n    String MESSAGES_ANDROID_PACKAGE_NAME = \"com.google.android.apps.messaging\";\n\n    // Phenotype package names\n    String DIALER_PHENOTYPE_PACKAGE_NAME = \"com.google.android.dialer\";\n    String MESSAGES_PHENOTYPE_PACKAGE_NAME = \"com.google.android.apps.messaging#com.google.android.apps.messaging\";\n\n    // Google Play links\n    String GOOGLE_PLAY_DETAILS_LINK = \"https://play.google.com/store/apps/details?id=\";\n    String GOOGLE_PLAY_BETA_LINK = \"https://play.google.com/apps/testing/\";\n\n    // Data / data folders\n    String DATA_DATA_PREFIX = \"/data/data/\";\n    String DIALER_CALLRECORDINGPROMPT = DATA_DATA_PREFIX + DIALER_ANDROID_PACKAGE_NAME + \"/files/callrecordingprompt\";\n    String GMS_PHENOTYPE_DB = DATA_DATA_PREFIX + GMS_ANDROID_PACKAGE_NAME + \"/databases/phenotype.db\";\n    String VENDING_PHENOTYPE_DB = DATA_DATA_PREFIX + VENDING_ANDROID_PACKAGE_NAME + \"/databases/phenotype.db\";\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/data/PhenotypeDBPackageName.java",
    "content": "package com.jacopomii.gappsmod.data;\n\npublic class PhenotypeDBPackageName {\n    private final String mPhenotypePackageName;\n    private final String mAndroidPackageName;\n\n    public PhenotypeDBPackageName(String phenotypePackageName, String androidPackageName) {\n        mPhenotypePackageName = phenotypePackageName;\n        mAndroidPackageName = androidPackageName;\n    }\n\n    public String getPhenotypePackageName() {\n        return mPhenotypePackageName;\n    }\n\n    public String getAndroidPackageName() {\n        return mAndroidPackageName;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/data/Version.java",
    "content": "package com.jacopomii.gappsmod.data;\n\npublic class Version implements Comparable<Version> {\n    private final String mVersion;\n\n    public Version(String version) {\n        if (version == null)\n            throw new IllegalArgumentException(\"Version can not be null\");\n        if (!version.matches(\"\\\\d+(\\\\.\\\\d+)*\"))\n            throw new IllegalArgumentException(\"Invalid version format\");\n        mVersion = version;\n    }\n\n    public final String getVersion() {\n        return mVersion;\n    }\n\n    @Override\n    public int compareTo(Version that) {\n        if(that == null)\n            return 1;\n        String[] thisParts = this.getVersion().split(\"\\\\.\");\n        String[] thatParts = that.getVersion().split(\"\\\\.\");\n        int length = Math.max(thisParts.length, thatParts.length);\n        for(int i = 0; i < length; i++) {\n            int thisPart = i < thisParts.length ?\n                    Integer.parseInt(thisParts[i]) : 0;\n            int thatPart = i < thatParts.length ?\n                    Integer.parseInt(thatParts[i]) : 0;\n            if(thisPart < thatPart)\n                return -1;\n            if(thisPart > thatPart)\n                return 1;\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/service/CoreRootService.java",
    "content": "package com.jacopomii.gappsmod.service;\n\nimport static com.jacopomii.gappsmod.data.Constants.DATA_DATA_PREFIX;\nimport static com.jacopomii.gappsmod.data.Constants.GMS_PHENOTYPE_DB;\nimport static com.jacopomii.gappsmod.data.Constants.VENDING_ANDROID_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.data.Constants.VENDING_PHENOTYPE_DB;\nimport static com.jacopomii.gappsmod.util.Utils.createInQueryString;\nimport static org.sqlite.database.sqlite.SQLiteDatabase.OPEN_READWRITE;\nimport static org.sqlite.database.sqlite.SQLiteDatabase.openDatabase;\n\nimport android.content.ContentValues;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.IBinder;\nimport android.os.Process;\n\nimport androidx.annotation.NonNull;\n\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.topjohnwu.superuser.Shell;\nimport com.topjohnwu.superuser.ipc.RootService;\nimport com.topjohnwu.superuser.nio.ExtendedFile;\nimport com.topjohnwu.superuser.nio.FileSystemManager;\n\nimport org.apache.commons.io.FileUtils;\nimport org.sqlite.database.sqlite.SQLiteDatabase;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n@SuppressWarnings(\"SameParameterValue\")\npublic class CoreRootService extends RootService {\n    static {\n        // Only load the library when this class is loaded in a root process.\n        // The classloader will load this class (and call this static block) in the non-root process because we accessed it when constructing the Intent to send.\n        // Add this check so we don't unnecessarily load native code that'll never be used.\n        if (Process.myUid() == 0)\n            System.loadLibrary(\"sqliteX\");\n    }\n\n    private SQLiteDatabase GMSPhenotypeDB = null;\n    private SQLiteDatabase VendingPhenotypeDB = null;\n\n    @Override\n    public IBinder onBind(@NonNull Intent intent) {\n        try {\n            GMSPhenotypeDB = openDatabase(GMS_PHENOTYPE_DB, null, OPEN_READWRITE);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        try {\n            VendingPhenotypeDB = openDatabase(VENDING_PHENOTYPE_DB, null, OPEN_READWRITE);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return new CoreRootServiceIPC();\n    }\n\n    @Override\n    public boolean onUnbind(@NonNull Intent intent) {\n        if (GMSPhenotypeDB != null && GMSPhenotypeDB.isOpen()) GMSPhenotypeDB.close();\n        if (VendingPhenotypeDB != null && VendingPhenotypeDB.isOpen()) VendingPhenotypeDB.close();\n        super.onUnbind(intent);\n        return false;\n    }\n\n    @Override\n    public void onDestroy() {\n        if (GMSPhenotypeDB != null && GMSPhenotypeDB.isOpen()) GMSPhenotypeDB.close();\n        if (VendingPhenotypeDB != null && VendingPhenotypeDB.isOpen()) VendingPhenotypeDB.close();\n        super.onDestroy();\n    }\n\n    private class CoreRootServiceIPC extends ICoreRootService.Stub {\n        @Override\n        public IBinder getFileSystemService() {\n            return FileSystemManager.getService();\n        }\n\n        @Override\n        public Map<String, String> phenotypeDBGetAllPackageNames() {\n            return CoreRootService.this.phenotypeDBGetAllPackageNames();\n        }\n\n        @Override\n        public Map<String, String> phenotypeDBGetAllOverriddenPackageNames() {\n            return CoreRootService.this.phenotypeDBGetAllOverriddenPackageNames();\n        }\n\n        @Override\n        public String phenotypeDBGetAndroidPackageNameByPhenotypePackageName(String phenotypePackageName) {\n            return CoreRootService.this.phenotypeDBGetAndroidPackageNameByPhenotypePackageName(phenotypePackageName);\n        }\n\n        @Override\n        public Map<String, List<Object>> phenotypeDBGetBooleanFlagsOrOverridden(String phenotypePackageName) {\n            return CoreRootService.this.phenotypeDBGetBooleanFlagsOrOverridden(phenotypePackageName);\n        }\n\n        @Override\n        public boolean phenotypeDBAreAllFlagsOverridden(String phenotypePackageName, List<String> flags) {\n            return CoreRootService.this.phenotypeDBAreAllFlagsOverridden(phenotypePackageName, flags);\n        }\n\n        @Override\n        public void phenotypeDBDeleteAllFlagOverrides() {\n            CoreRootService.this.phenotypeDBDeleteAllFlagOverrides(true);\n        }\n\n        @Override\n        public void phenotypeDBDeleteAllFlagOverridesByPhenotypePackageName(String phenotypePackageName) {\n            CoreRootService.this.phenotypeDBDeleteAllFlagOverridesByPhenotypePackageName(phenotypePackageName, true);\n        }\n\n        @Override\n        public void phenotypeDBDeleteFlagOverrides(String phenotypePackageName, List<String> flags) {\n            CoreRootService.this.phenotypeDBDeleteFlagOverrides(phenotypePackageName, flags, true);\n        }\n\n        @Override\n        public void phenotypeDBOverrideBooleanFlag(String phenotypePackageName, String flag, boolean value) {\n            CoreRootService.this.phenotypeDBOverrideBooleanFlag(phenotypePackageName, flag, value, true);\n        }\n\n        @Override\n        public void phenotypeDBOverrideExtensionFlag(String phenotypePackageName, String flag, byte[] value) {\n            CoreRootService.this.phenotypeDBOverrideExtensionFlag(phenotypePackageName, flag, value, true);\n        }\n\n        @Override\n        public void phenotypeDBOverrideStringFlag(String phenotypePackageName, String flag, String value) {\n            CoreRootService.this.phenotypeDBOverrideStringFlag(phenotypePackageName, flag, value, true);\n        }\n    }\n\n\n    private Map<String, String> phenotypeDBGetAllPackageNames() {\n        HashMap<String, String> map = new HashMap<>();\n\n        String sql = \"SELECT Flags.packageName as phenotypePackageName, Packages.androidPackageName \" +\n                \"FROM Flags, Packages \" +\n                \"WHERE phenotypePackageName=Packages.packageName \" +\n                \"GROUP BY phenotypePackageName \" +\n                \"ORDER BY phenotypePackageName ASC\";\n\n        try {\n            try (Cursor cursor = GMSPhenotypeDB.rawQuery(sql, null)) {\n                while (cursor.moveToNext())\n                    map.put(cursor.getString(0), cursor.getString(1));\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        try {\n            try (Cursor cursor = VendingPhenotypeDB.rawQuery(sql, null)) {\n                while (cursor.moveToNext())\n                    map.put(cursor.getString(0), cursor.getString(1));\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return map;\n    }\n\n    private Map<String, String> phenotypeDBGetAllOverriddenPackageNames() {\n        HashMap<String, String> map = new HashMap<>();\n\n        String sql = \"SELECT FlagOverrides.packageName as phenotypePackageName, Packages.androidPackageName \" +\n                \"FROM FlagOverrides, Packages \" +\n                \"WHERE phenotypePackageName=Packages.packageName \" +\n                \"GROUP BY phenotypePackageName \" +\n                \"ORDER BY phenotypePackageName ASC\";\n\n        try {\n            try (Cursor cursor = GMSPhenotypeDB.rawQuery(sql, null)) {\n                while (cursor.moveToNext())\n                    map.put(cursor.getString(0), cursor.getString(1));\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        try {\n            try (Cursor cursor = VendingPhenotypeDB.rawQuery(sql, null)) {\n                while (cursor.moveToNext())\n                    map.put(cursor.getString(0), cursor.getString(1));\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return map;\n    }\n\n    private String phenotypeDBGetAndroidPackageNameByPhenotypePackageName(String phenotypePackageName) {\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String sql = \"SELECT androidPackageName FROM Packages WHERE packageName=? LIMIT 1\";\n        String[] selectionArgs = {phenotypePackageName};\n\n        try {\n            try (Cursor cursor = phenotypeDB.rawQuery(sql, selectionArgs)) {\n                if (cursor.moveToFirst()) return cursor.getString(0);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return \"\";\n    }\n\n    private Map<String, List<Object>> phenotypeDBGetBooleanFlagsOrOverridden(String phenotypePackageName) {\n        Map<String, List<Object>> map = new HashMap<>();\n\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String sql = \"SELECT DISTINCT f.name AS name, COALESCE(fo.boolVal, f.boolVal) AS boolVal, CASE WHEN fo.boolVal != f.boolVal THEN 1 ELSE 0 END AS changed \" +\n                \"FROM Flags f \" +\n                \"LEFT JOIN FlagOverrides fo ON f.packageName = fo.packageName AND f.name = fo.name \" +\n                \"WHERE f.packageName = ? AND f.user = '' AND (f.boolVal IS NOT NULL OR fo.boolVal IS NOT NULL)\" +\n                \"UNION ALL \" +\n                \"SELECT DISTINCT fo.name AS name, fo.boolVal, 1 AS changed \" +\n                \"FROM FlagOverrides fo \" +\n                \"LEFT JOIN Flags f ON f.packageName = fo.packageName AND f.name = fo.name \" +\n                \"WHERE fo.packageName = ? AND f.name IS NULL AND fo.user = '' AND fo.boolVal IS NOT NULL\";\n        String[] selectionArgs = {phenotypePackageName, phenotypePackageName};\n\n        try {\n            try (Cursor cursor = phenotypeDB.rawQuery(sql, selectionArgs)) {\n                while (cursor.moveToNext()) {\n                    String name = cursor.getString(0);\n                    Boolean boolVal = cursor.getInt(1) != 0;\n                    Boolean changed = cursor.getInt(2) != 0;\n                    map.put(name, Arrays.asList(boolVal, changed));\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return map;\n    }\n\n    private boolean phenotypeDBAreAllFlagsOverridden(String phenotypePackageName, List<String> flags) {\n        boolean areAllFlagsOverridden = false;\n\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String sql = \"SELECT DISTINCT name FROM FlagOverrides WHERE packageName=? AND name IN (\" + createInQueryString(flags.size()) + \")\";\n        List<String> selectionArgs = new ArrayList<>();\n        selectionArgs.add(phenotypePackageName);\n        selectionArgs.addAll(flags);\n\n        try {\n            try (Cursor cursor = phenotypeDB.rawQuery(sql, selectionArgs.toArray(new String[0]))) {\n                areAllFlagsOverridden = cursor.getCount() == flags.size();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return areAllFlagsOverridden;\n    }\n\n    private void phenotypeDBDeleteAllFlagOverrides(boolean deletePackagePhenotypeCache) {\n        Set<String> overriddenPhenotypePackageNames = phenotypeDBGetAllOverriddenPackageNames().keySet();\n\n        try {\n            GMSPhenotypeDB.delete(\"FlagOverrides\", null, null);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        try {\n            VendingPhenotypeDB.delete(\"FlagOverrides\", null, null);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (deletePackagePhenotypeCache) {\n            for (String phenotypePackageName : overriddenPhenotypePackageNames) {\n                killPackageAndDeletePhenotypeCache(phenotypePackageName);\n            }\n        }\n    }\n\n    private void phenotypeDBDeleteAllFlagOverridesByPhenotypePackageName(String phenotypePackageName, boolean deletePackagePhenotypeCache) {\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n\n        try {\n            phenotypeDB.delete(\"FlagOverrides\", \"packageName=?\", new String[]{phenotypePackageName});\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (deletePackagePhenotypeCache)\n            killPackageAndDeletePhenotypeCache(phenotypePackageName);\n    }\n\n    private void phenotypeDBDeleteFlagOverrides(String phenotypePackageName, List<String> flags, boolean deletePackagePhenotypeCache) {\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String whereClause = \"packageName=? AND name IN (\" + createInQueryString(flags.size()) + \")\";\n        List<String> whereArgs = new ArrayList<>();\n        whereArgs.add(phenotypePackageName);\n        whereArgs.addAll(flags);\n\n        try {\n            phenotypeDB.delete(\"FlagOverrides\", whereClause, whereArgs.toArray(new String[0]));\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (deletePackagePhenotypeCache)\n            killPackageAndDeletePhenotypeCache(phenotypePackageName);\n    }\n\n    private void phenotypeDBOverrideBooleanFlag(String phenotypePackageName, String flag, boolean value, boolean deletePackagePhenotypeCache) {\n        phenotypeDBDeleteFlagOverrides(phenotypePackageName, Collections.singletonList(flag), false);\n\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String sql = \"SELECT DISTINCT user FROM Flags WHERE packageName = ?\";\n        String[] selectionArgs = {phenotypePackageName};\n\n        try {\n            try (Cursor cursor = phenotypeDB.rawQuery(sql, selectionArgs)) {\n                while (cursor.moveToNext()) {\n                    ContentValues contentValues = new ContentValues();\n                    contentValues.put(\"packageName\", phenotypePackageName);\n                    contentValues.put(\"flagType\", 0);\n                    contentValues.put(\"name\", flag);\n                    contentValues.put(\"user\", cursor.getString(0));\n                    contentValues.put(\"boolVal\", (value ? \"1\" : \"0\"));\n                    contentValues.put(\"committed\", 0);\n                    phenotypeDB.insertWithOnConflict(\"FlagOverrides\", null, contentValues, SQLiteDatabase.CONFLICT_REPLACE);\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (deletePackagePhenotypeCache)\n            killPackageAndDeletePhenotypeCache(phenotypePackageName);\n    }\n\n    private void phenotypeDBOverrideExtensionFlag(String phenotypePackageName, String flag, byte[] value, boolean deletePackagePhenotypeCache) {\n        phenotypeDBDeleteFlagOverrides(phenotypePackageName, Collections.singletonList(flag), false);\n\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String sql = \"SELECT DISTINCT user FROM Flags WHERE packageName = ?\";\n        String[] selectionArgs = {phenotypePackageName};\n\n        try {\n            try (Cursor cursor = phenotypeDB.rawQuery(sql, selectionArgs)) {\n                while (cursor.moveToNext()) {\n                    ContentValues contentValues = new ContentValues();\n                    contentValues.put(\"packageName\", phenotypePackageName);\n                    contentValues.put(\"flagType\", 0);\n                    contentValues.put(\"name\", flag);\n                    contentValues.put(\"user\", cursor.getString(0));\n                    contentValues.put(\"extensionVal\", value);\n                    contentValues.put(\"committed\", 0);\n                    phenotypeDB.insertWithOnConflict(\"FlagOverrides\", null, contentValues, SQLiteDatabase.CONFLICT_REPLACE);\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (deletePackagePhenotypeCache)\n            killPackageAndDeletePhenotypeCache(phenotypePackageName);\n    }\n\n    private void phenotypeDBOverrideStringFlag(String phenotypePackageName, String flag, String value, boolean deletePackagePhenotypeCache) {\n        phenotypeDBDeleteFlagOverrides(phenotypePackageName, Collections.singletonList(flag), false);\n\n        SQLiteDatabase phenotypeDB = getPhenotypeDBByPhenotypePackageName(phenotypePackageName);\n        String sql = \"SELECT DISTINCT user FROM Flags WHERE packageName = ?\";\n        String[] selectionArgs = {phenotypePackageName};\n\n        try {\n            try (Cursor cursor = phenotypeDB.rawQuery(sql, selectionArgs)) {\n                while (cursor.moveToNext()) {\n                    ContentValues contentValues = new ContentValues();\n                    contentValues.put(\"packageName\", phenotypePackageName);\n                    contentValues.put(\"flagType\", 0);\n                    contentValues.put(\"name\", flag);\n                    contentValues.put(\"user\", cursor.getString(0));\n                    contentValues.put(\"stringVal\", value);\n                    contentValues.put(\"committed\", 0);\n                    phenotypeDB.insertWithOnConflict(\"FlagOverrides\", null, contentValues, SQLiteDatabase.CONFLICT_REPLACE);\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (deletePackagePhenotypeCache)\n            killPackageAndDeletePhenotypeCache(phenotypePackageName);\n    }\n\n    /**\n     * Get the correct database (GMS or Vending) based on the {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name for which to get the\n     *                             Phenotype DB.\n     * @return a {@code SQLiteDatabase} reference to the GMS or Vending Phenotype DB.\n     * The returned object may be {@code null} if there was an error opening the database.\n     */\n    private SQLiteDatabase getPhenotypeDBByPhenotypePackageName(String phenotypePackageName) {\n        if (phenotypePackageName.equals(\"com.google.android.finsky.regular\") || phenotypePackageName.equals(\"com.google.android.finsky.stable\")) {\n            return VendingPhenotypeDB;\n        } else {\n            return GMSPhenotypeDB;\n        }\n    }\n\n    /**\n     * Kill and delete the Phenotype cache files of the Android application corresponding to the\n     * given {@code phenotypePackageName}.\n     *\n     * @param phenotypePackageName the Phenotype (not Android) package name to kill and to delete\n     *                             the Phenotype cache files for.\n     */\n    private void killPackageAndDeletePhenotypeCache(String phenotypePackageName) {\n        String androidPackageName = phenotypeDBGetAndroidPackageNameByPhenotypePackageName(phenotypePackageName);\n\n        // Kill the android application corresponding to the phenotypePackageName\n        Shell.cmd(\"am kill all \" + androidPackageName).exec();\n\n        // Delete application phenotype Cache\n        ExtendedFile phenotypeCache = FileSystemManager.getLocal().getFile(DATA_DATA_PREFIX + androidPackageName + \"/files/phenotype\");\n        if (phenotypeCache.exists()) {\n            try {\n                FileUtils.deleteDirectory(phenotypeCache);\n            } catch (IOException | IllegalArgumentException e) {\n                e.printStackTrace();\n            }\n        }\n\n        // If the application is Vending, additional cache files need to be deleted\n        if (androidPackageName.equals(VENDING_ANDROID_PACKAGE_NAME)) {\n            ExtendedFile[] VendingFiles = FileSystemManager.getLocal().getFile(DATA_DATA_PREFIX + androidPackageName + \"/files\").listFiles();\n            if (VendingFiles != null) {\n                for (ExtendedFile file : VendingFiles) {\n                    if (file.getName().startsWith(\"experiment-flags\")) {\n                        //noinspection ResultOfMethodCallIgnored\n                        file.delete();\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/activity/MainActivity.java",
    "content": "package com.jacopomii.gappsmod.ui.activity;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.drawerlayout.widget.DrawerLayout;\nimport androidx.navigation.NavController;\nimport androidx.navigation.Navigation;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.navigation.ui.AppBarConfiguration;\nimport androidx.navigation.ui.NavigationUI;\n\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.ActivityMainBinding;\nimport com.jacopomii.gappsmod.service.CoreRootService;\nimport com.topjohnwu.superuser.ipc.RootService;\nimport com.topjohnwu.superuser.nio.FileSystemManager;\n\npublic class MainActivity extends AppCompatActivity {\n    private AppBarConfiguration mAppBarConfiguration;\n    private ActivityMainBinding mBinding;\n\n    private boolean mCoreRootServiceBound = false;\n    private ServiceConnection mCoreRootServiceConnection;\n    private ICoreRootService mCoreRootServiceIpc;\n    private FileSystemManager mCoreRootServiceFSManager;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        // The savedInstanceState must not be used, otherwise the views (and the fragments contained\n        // by this activity) are restored before the RootService is started, causing NPE.\n        super.onCreate(null);\n\n        // Enable edge-to-edge: allows drawing under system bars, preventing Android from\n        // automatically applying the fitSystemWindows property to the root view.\n        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);\n\n        // Start CoreRootService connection\n        Intent intent = new Intent(this, CoreRootService.class);\n        mCoreRootServiceConnection = new ServiceConnection() {\n            @Override\n            public void onServiceConnected(ComponentName name, IBinder service) {\n                try {\n                    // Set references to the remote coreRootService\n                    mCoreRootServiceBound = true;\n                    mCoreRootServiceIpc = ICoreRootService.Stub.asInterface(service);\n                    mCoreRootServiceFSManager = FileSystemManager.getRemote(mCoreRootServiceIpc.getFileSystemService());\n\n                    // Inflate the activity layout and set the content view\n                    mBinding = ActivityMainBinding.inflate(getLayoutInflater());\n                    setContentView(mBinding.getRoot());\n\n                    // Set the toolbar\n                    setSupportActionBar(mBinding.toolbar);\n\n                    // Set the drawer\n                    DrawerLayout drawer = mBinding.drawerLayout;\n                    mAppBarConfiguration = new AppBarConfiguration.Builder(\n                            R.id.nav_suggested_mods,\n                            R.id.nav_boolean_mods,\n                            R.id.nav_revert_mods,\n                            R.id.nav_information\n                    ).setOpenableLayout(drawer).build();\n\n                    // Pass through the window insets to the navHostFragment child views, except the top system bar\n                    ViewCompat.setOnApplyWindowInsetsListener(mBinding.navHostFragment, (view, insets) -> {\n                        WindowInsetsCompat insetsCompat = new WindowInsetsCompat.Builder(insets)\n                                .setInsets(WindowInsetsCompat.Type.systemBars(), Insets.of(\n                                        insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime()).left,\n                                        0,\n                                        insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime()).right,\n                                        insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime()).bottom))\n                                .build();\n                        return ViewCompat.onApplyWindowInsets(view, insetsCompat);\n                    });\n\n                    // Set the navigation controller\n                    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(mBinding.navHostFragment.getId());\n                    if (navHostFragment != null) {\n                        NavController navController = navHostFragment.getNavController();\n                        NavigationUI.setupActionBarWithNavController(MainActivity.this, navController, mAppBarConfiguration);\n                        NavigationUI.setupWithNavController(mBinding.navView, navController);\n                    }\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n\n            @Override\n            public void onServiceDisconnected(ComponentName name) {\n                mCoreRootServiceBound = false;\n                mCoreRootServiceIpc = null;\n                mCoreRootServiceFSManager = null;\n            }\n        };\n        RootService.bind(intent, mCoreRootServiceConnection);\n    }\n\n    public FileSystemManager getCoreRootServiceFSManager() {\n        return mCoreRootServiceFSManager;\n    }\n\n    public ICoreRootService getCoreRootServiceIpc() {\n        return mCoreRootServiceIpc;\n    }\n\n    @Override\n    public boolean onSupportNavigateUp() {\n        NavController navController = Navigation.findNavController(this, mBinding.navHostFragment.getId());\n        return NavigationUI.navigateUp(navController, mAppBarConfiguration)\n                || super.onSupportNavigateUp();\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (mBinding.drawerLayout.isOpen())\n            mBinding.drawerLayout.close();\n        else\n            finishAffinity();\n    }\n\n    @Override\n    protected void onDestroy() {\n        if (mCoreRootServiceBound)\n            RootService.unbind(mCoreRootServiceConnection);\n        super.onDestroy();\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/activity/SplashScreenActivity.java",
    "content": "package com.jacopomii.gappsmod.ui.activity;\n\nimport static com.jacopomii.gappsmod.data.Constants.GMS_ANDROID_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.data.Constants.GOOGLE_PLAY_DETAILS_LINK;\nimport static com.jacopomii.gappsmod.data.Constants.GMS_PHENOTYPE_DB;\nimport static com.jacopomii.gappsmod.util.Utils.checkUpdateAvailable;\nimport static com.jacopomii.gappsmod.util.Utils.openGooglePlay;\n\nimport android.annotation.SuppressLint;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.google.android.material.progressindicator.CircularProgressIndicator;\nimport com.jacopomii.gappsmod.BuildConfig;\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.ActivitySplashScreenBinding;\nimport com.jacopomii.gappsmod.service.CoreRootService;\nimport com.topjohnwu.superuser.Shell;\nimport com.topjohnwu.superuser.ipc.RootService;\nimport com.topjohnwu.superuser.nio.FileSystemManager;\n\nimport java.util.concurrent.CountDownLatch;\n\n@SuppressLint(\"CustomSplashScreen\")\npublic class SplashScreenActivity extends AppCompatActivity {\n    static {\n        // Set Libsu settings before creating the main shell\n        Shell.enableVerboseLogging = BuildConfig.DEBUG;\n        Shell.setDefaultBuilder(Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER));\n    }\n\n    private ActivitySplashScreenBinding mBinding;\n\n    private final CountDownLatch mRootCheckPassed = new CountDownLatch(1);\n    private final CountDownLatch mCoreRootServiceConnected = new CountDownLatch(1);\n    private final CountDownLatch mGMSPhenotypeCheckPassed = new CountDownLatch(1);\n    private final CountDownLatch mUpdateCheckFinished = new CountDownLatch(1);\n\n    private boolean mCoreRootServiceBound = false;\n    private ServiceConnection mCoreRootServiceConnection;\n    private FileSystemManager mCoreRootServiceFSManager;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        // Inflate the activity layout and set the content view\n        mBinding = ActivitySplashScreenBinding.inflate(getLayoutInflater());\n        setContentView(R.layout.activity_splash_screen);\n\n        // Start CoreRootService connection\n        Intent intent = new Intent(this, CoreRootService.class);\n        mCoreRootServiceConnection = new ServiceConnection() {\n            @Override\n            public void onServiceConnected(ComponentName name, IBinder service) {\n                // Set references to the remote coreRootService\n                mCoreRootServiceBound = true;\n                ICoreRootService ipc = ICoreRootService.Stub.asInterface(service);\n                try {\n                    mCoreRootServiceFSManager = FileSystemManager.getRemote(ipc.getFileSystemService());\n                    mCoreRootServiceConnected.countDown();\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n\n                // Update the UI\n                setCheckUIDone(mBinding.circularRootService.getId(), mBinding.doneRootService.getId(), mCoreRootServiceConnected.getCount() == 0);\n            }\n\n            @Override\n            public void onServiceDisconnected(ComponentName name) {\n                mCoreRootServiceBound = false;\n                mCoreRootServiceFSManager = null;\n            }\n        };\n        RootService.bind(intent, mCoreRootServiceConnection);\n\n        // Root permission check\n        new Thread() {\n            @Override\n            public void run() {\n                // Check root\n                if (checkRoot()) {\n                    mRootCheckPassed.countDown();\n                } else {\n                    runOnUiThread(() ->\n                            new MaterialAlertDialogBuilder(SplashScreenActivity.this)\n                                    .setCancelable(false)\n                                    .setMessage(R.string.root_access_denied)\n                                    .setPositiveButton(R.string.exit, (dialog, i) -> System.exit(0))\n                                    .show());\n                }\n\n                // Update the UI\n                setCheckUIDone(mBinding.circularRoot.getId(), mBinding.doneRoot.getId(), mRootCheckPassed.getCount() == 0);\n            }\n        }.start();\n\n        // GMS Phenotype DB check\n        new Thread() {\n            @Override\n            public void run() {\n                try {\n                    // Wait for root check to pass\n                    mRootCheckPassed.await();\n                    // Wait for coreRootService to connect\n                    mCoreRootServiceConnected.await();\n\n                    // Check the GMS Phenotype DB\n                    if (checkGMSPhenotypeDB()) {\n                        mGMSPhenotypeCheckPassed.countDown();\n                    } else {\n                        runOnUiThread(() ->\n                                new MaterialAlertDialogBuilder(SplashScreenActivity.this)\n                                        .setCancelable(false)\n                                        .setMessage(getString(R.string.phenotype_db_does_not_exist_gms))\n                                        .setPositiveButton(R.string.install, (dialogInterface, i) -> openGooglePlay(SplashScreenActivity.this, GOOGLE_PLAY_DETAILS_LINK + GMS_ANDROID_PACKAGE_NAME))\n                                        .setNegativeButton(R.string.exit, (dialog, which) -> System.exit(0))\n                                        .setNeutralButton(R.string.continue_anyway, (dialogInterface, i) -> mGMSPhenotypeCheckPassed.countDown())\n                                        .show());\n                    }\n\n                    // Update the UI\n                    setCheckUIDone(mBinding.circularPhenotypeGms.getId(), mBinding.donePhenotypeGms.getId(), mGMSPhenotypeCheckPassed.getCount() == 0);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }.start();\n\n        // Update available check\n        new Thread() {\n            @Override\n            public void run() {\n                // Check if updates are available\n                if (!checkUpdateAvailable(SplashScreenActivity.this)) {\n                    mUpdateCheckFinished.countDown();\n                } else {\n                    runOnUiThread(() ->\n                            new MaterialAlertDialogBuilder(SplashScreenActivity.this)\n                                    .setCancelable(false)\n                                    .setMessage(R.string.new_version_alert)\n                                    .setPositiveButton(\n                                            R.string.github,\n                                            (dialogInterface, i) -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github_link) + \"/releases\")))\n                                    )\n                                    .setNegativeButton(R.string.continue_anyway, (dialogInterface, i) -> mUpdateCheckFinished.countDown())\n                                    .show());\n                }\n\n                // Update the UI\n                setCheckUIDone(mBinding.circularUpdates.getId(), mBinding.doneUpdates.getId(), mUpdateCheckFinished.getCount() == 0);\n            }\n        }.start();\n\n        // End splash screen and go to the main activity\n        new Thread() {\n            @Override\n            public void run() {\n                try {\n                    // Wait for all checks to pass and for all operations to finish\n                    mRootCheckPassed.await();\n                    mCoreRootServiceConnected.await();\n                    mGMSPhenotypeCheckPassed.await();\n                    mUpdateCheckFinished.await();\n\n                    // This is just for aesthetics: I don't want the splashscreen to be too fast\n                    Thread.sleep(1000);\n\n                    // Start the main activity\n                    Intent intent = new Intent(SplashScreenActivity.this, MainActivity.class);\n                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);\n                    startActivity(intent);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }.start();\n    }\n\n    private boolean checkRoot() {\n        return Shell.getShell().isRoot();\n    }\n\n    private boolean checkGMSPhenotypeDB() {\n        return mCoreRootServiceFSManager.getFile(GMS_PHENOTYPE_DB).exists();\n    }\n\n    private void setCheckUIDone(int circularID, int doneImageID, boolean success) {\n        CircularProgressIndicator circular = findViewById(circularID);\n        ImageView doneImage = findViewById(doneImageID);\n        runOnUiThread(() -> {\n            circular.setVisibility(View.GONE);\n            doneImage.setImageResource(success ? R.drawable.ic_success_24 : R.drawable.ic_fail_24);\n            doneImage.setVisibility(View.VISIBLE);\n        });\n    }\n\n    @Override\n    protected void onDestroy() {\n        if (mCoreRootServiceBound)\n            RootService.unbind(mCoreRootServiceConnection);\n        super.onDestroy();\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/adapter/BooleanModsRecyclerViewAdapter.java",
    "content": "package com.jacopomii.gappsmod.ui.adapter;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.os.RemoteException;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.widget.Filter;\nimport android.widget.Filterable;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.card.MaterialCardView;\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.data.BooleanFlag;\nimport com.jacopomii.gappsmod.databinding.SwitchCardBinding;\nimport com.jacopomii.gappsmod.ui.view.ProgrammaticMaterialSwitchView;\nimport com.l4digital.fastscroll.FastScroller;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n@SuppressWarnings(\"unchecked\")\npublic class BooleanModsRecyclerViewAdapter extends RecyclerView.Adapter<BooleanModsRecyclerViewAdapter.ViewHolder> implements Filterable, FastScroller.SectionIndexer {\n    private final Context mContext;\n\n    private List<BooleanFlag> mFlagsList = new ArrayList<>();\n    private List<BooleanFlag> mFlagsListFiltered = new ArrayList<>();\n    private String mPhenotypePackageName = null;\n    private CharSequence mLastFilterPerformed = null;\n\n    private final ICoreRootService mCoreRootServiceIpc;\n\n    public BooleanModsRecyclerViewAdapter(Context context, ICoreRootService coreRootServiceIpc) {\n        mContext = context;\n        mCoreRootServiceIpc = coreRootServiceIpc;\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    public void selectPhenotypePackageName(String phenotypePackageName) {\n        mPhenotypePackageName = phenotypePackageName;\n\n        try {\n            mFlagsList = new ArrayList<>();\n            TreeMap<String, List<Object>> map = new TreeMap<String, List<Object>>(mCoreRootServiceIpc.phenotypeDBGetBooleanFlagsOrOverridden(phenotypePackageName));\n            for (Map.Entry<String, List<Object>> flag : map.entrySet()) {\n                String flagName = flag.getKey();\n                List<Object> flagData = flag.getValue();\n                Boolean flagValue = (Boolean) flagData.get(0);\n                Boolean flagOverriddenAndChanged = (Boolean) flagData.get(1);\n                mFlagsList.add(new BooleanFlag(flagName, flagValue, flagOverriddenAndChanged));\n            }\n\n            if (mLastFilterPerformed != null) {\n                getFilter().filter(mLastFilterPerformed);\n            } else {\n                mFlagsListFiltered = mFlagsList;\n                notifyDataSetChanged();\n            }\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @NonNull\n    @Override\n    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        // Initialize binding and viewHolder\n        SwitchCardBinding binding = SwitchCardBinding.inflate(LayoutInflater.from(mContext), parent, false);\n        ViewHolder viewHolder = new ViewHolder(binding);\n\n        // Set setOnCheckedChangeListener on list items\n        viewHolder.mSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {\n            int checkedPosition = viewHolder.getAdapterPosition();\n            BooleanFlag checkedBooleanFlag = mFlagsListFiltered.get(checkedPosition);\n            String checkedBooleanFlagName = checkedBooleanFlag.getFlagName();\n\n            checkedBooleanFlag.setFlagValue(isChecked);\n            try {\n                mCoreRootServiceIpc.phenotypeDBOverrideBooleanFlag(mPhenotypePackageName, checkedBooleanFlagName, isChecked);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            checkedBooleanFlag.setFlagOverriddenAndChanged(!checkedBooleanFlag.getFlagOverriddenAndChanged());\n\n            notifyItemChanged(checkedPosition);\n        });\n\n        // Return viewHolder\n        return viewHolder;\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {\n        // Get the boolean flag\n        BooleanFlag booleanFlag = mFlagsListFiltered.get(position);\n\n        // Update switch text\n        holder.mTextView.setText(booleanFlag.getFlagName());\n\n        // Update the switch checked status without triggering any existing listener\n        holder.mSwitch.setCheckedProgrammatically(booleanFlag.getFlagValue());\n\n        // Change background color for cards containing overridden and changed flags\n        TypedArray typedArray = mContext.getTheme().obtainStyledAttributes(R.styleable.ViewStyle);\n        int colorSurface = typedArray.getColor(R.styleable.ViewStyle_colorSurface, Color.WHITE);\n        int colorSurfaceVariant = typedArray.getColor(R.styleable.ViewStyle_colorSecondaryContainer, Color.LTGRAY);\n        typedArray.recycle();\n        int cardBackgroundColor = booleanFlag.getFlagOverriddenAndChanged() ? colorSurfaceVariant : colorSurface;\n        ((MaterialCardView) holder.itemView).setCardBackgroundColor(cardBackgroundColor);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mFlagsListFiltered.size();\n    }\n\n    @Override\n    public Filter getFilter() {\n        return new Filter() {\n            @Override\n            protected FilterResults performFiltering(CharSequence charSequence) {\n                mLastFilterPerformed = charSequence;\n\n                try {\n                    JSONObject filterConfig = new JSONObject(charSequence.toString());\n                    String key = filterConfig.getString(\"key\");\n                    boolean enabled = filterConfig.getBoolean(\"enabled\");\n                    boolean disabled = filterConfig.getBoolean(\"disabled\");\n                    boolean changed = filterConfig.getBoolean(\"changed\");\n                    boolean unchanged = filterConfig.getBoolean(\"unchanged\");\n\n                    List<BooleanFlag> flagsListFiltered = new ArrayList<>();\n                    for (BooleanFlag booleanFlag : mFlagsList) {\n                        if (booleanFlag.getFlagName().toLowerCase().contains(key.toLowerCase())) {\n                            boolean flagValue = booleanFlag.getFlagValue();\n                            boolean flagChanged = booleanFlag.getFlagOverriddenAndChanged();\n                            if (((enabled && flagValue) || (disabled && !flagValue)) && ((changed && flagChanged) || (unchanged && !flagChanged)))\n                                flagsListFiltered.add(booleanFlag);\n                        }\n                    }\n                    mFlagsListFiltered = flagsListFiltered;\n                } catch (JSONException e) {\n                    e.printStackTrace();\n                }\n\n                FilterResults filterResults = new FilterResults();\n                filterResults.values = mFlagsListFiltered;\n                filterResults.count = mFlagsListFiltered.size();\n                return filterResults;\n            }\n\n            @SuppressLint(\"NotifyDataSetChanged\")\n            @Override\n            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {\n                mFlagsListFiltered = (List<BooleanFlag>) filterResults.values;\n                notifyDataSetChanged();\n            }\n        };\n    }\n\n    @Override\n    public CharSequence getSectionText(int position) {\n        return mFlagsListFiltered.get(position).getFlagName().substring(0, 1);\n    }\n\n    public static class ViewHolder extends RecyclerView.ViewHolder {\n        private final TextView mTextView;\n        private final ProgrammaticMaterialSwitchView mSwitch;\n\n        public ViewHolder(SwitchCardBinding binding) {\n            super(binding.getRoot());\n            mTextView = binding.switchCardTextview;\n            mSwitch = binding.switchCardSwitch;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/adapter/SelectPackageRecyclerViewAdapter.java",
    "content": "package com.jacopomii.gappsmod.ui.adapter;\n\nimport static com.jacopomii.gappsmod.util.Utils.getApplicationLabelOrUnknown;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.graphics.drawable.Drawable;\nimport android.os.RemoteException;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.widget.Filter;\nimport android.widget.Filterable;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.data.PhenotypeDBPackageName;\nimport com.jacopomii.gappsmod.databinding.PackageRowBinding;\nimport com.jacopomii.gappsmod.util.OnItemClickListener;\nimport com.l4digital.fastscroll.FastScroller;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n@SuppressWarnings(\"unchecked\")\npublic class SelectPackageRecyclerViewAdapter extends RecyclerView.Adapter<SelectPackageRecyclerViewAdapter.ViewHolder> implements Filterable, FastScroller.SectionIndexer {\n    private final Context mContext;\n\n    private List<PhenotypeDBPackageName> mPackageList = new ArrayList<>();\n    private List<PhenotypeDBPackageName> mPackageListFiltered = new ArrayList<>();\n\n    private final ICoreRootService mCoreRootServiceIpc;\n\n    private final OnItemClickListener mOnItemClickListener;\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    public SelectPackageRecyclerViewAdapter(Context context, ICoreRootService coreRootServiceIpc, OnItemClickListener onItemClickListener) {\n        mContext = context;\n        mCoreRootServiceIpc = coreRootServiceIpc;\n        mOnItemClickListener = onItemClickListener;\n\n        try {\n            mPackageList = new ArrayList<>();\n            TreeMap<String, String> map = new TreeMap<String, String>(mCoreRootServiceIpc.phenotypeDBGetAllPackageNames());\n            for (Map.Entry<String, String> packageName : map.entrySet())\n                mPackageList.add(new PhenotypeDBPackageName(packageName.getKey(), packageName.getValue()));\n\n            mPackageListFiltered = mPackageList;\n\n            notifyDataSetChanged();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @NonNull\n    @Override\n    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        // Initialize binding and viewHolder\n        PackageRowBinding binding = PackageRowBinding.inflate(LayoutInflater.from(mContext), parent, false);\n        ViewHolder viewHolder = new ViewHolder(binding);\n\n        // Set onClickListener on list rows\n        viewHolder.mRow.setOnClickListener(v -> {\n            int position = viewHolder.getAdapterPosition();\n            mOnItemClickListener.onItemClick(mPackageListFiltered.get(position).getPhenotypePackageName());\n        });\n\n        // Return viewHolder\n        return viewHolder;\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {\n        String phenotypePackageName = mPackageListFiltered.get(position).getPhenotypePackageName();\n        String androidPackageName = mPackageListFiltered.get(position).getAndroidPackageName();\n\n        PackageManager packageManager = mContext.getPackageManager();\n\n        Drawable packageIcon;\n        try {\n            packageIcon = packageManager.getApplicationIcon(androidPackageName);\n        } catch (PackageManager.NameNotFoundException e) {\n            packageIcon = AppCompatResources.getDrawable(mContext, R.drawable.ic_error_24);\n            if (packageIcon != null) {\n                TypedArray typedArray = mContext.getTheme().obtainStyledAttributes(R.styleable.ViewStyle);\n                int colorError = typedArray.getColor(R.styleable.ViewStyle_colorError, Color.RED);\n                typedArray.recycle();\n                packageIcon.mutate().setTint(colorError);\n            }\n        }\n        holder.mPackageIcon.setImageDrawable(packageIcon);\n\n        String appName = getApplicationLabelOrUnknown(mContext, androidPackageName);\n        holder.mAppName.setText(appName);\n\n        holder.mPhenotypePackageName.setText(phenotypePackageName);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mPackageListFiltered.size();\n    }\n\n    @Override\n    public Filter getFilter() {\n        return new Filter() {\n            @Override\n            protected FilterResults performFiltering(CharSequence charSequence) {\n                String keyLowercase = charSequence.toString().toLowerCase();\n\n                List<PhenotypeDBPackageName> packageListFiltered = new ArrayList<>();\n                for (PhenotypeDBPackageName phenotypeDBPackageName : mPackageList) {\n                    String phenotypePackageNameLowercase = phenotypeDBPackageName.getPhenotypePackageName().toLowerCase();\n                    String appNameLowercase = getApplicationLabelOrUnknown(mContext, phenotypeDBPackageName.getAndroidPackageName()).toLowerCase();\n                    if (phenotypePackageNameLowercase.contains(keyLowercase) || appNameLowercase.contains(keyLowercase))\n                        packageListFiltered.add(phenotypeDBPackageName);\n                }\n                mPackageListFiltered = packageListFiltered;\n\n                FilterResults filterResults = new FilterResults();\n                filterResults.values = mPackageListFiltered;\n                filterResults.count = mPackageListFiltered.size();\n                return filterResults;\n            }\n\n            @SuppressLint(\"NotifyDataSetChanged\")\n            @Override\n            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {\n                mPackageListFiltered = (List<PhenotypeDBPackageName>) filterResults.values;\n                notifyDataSetChanged();\n            }\n        };\n    }\n\n    @Override\n    public CharSequence getSectionText(int position) {\n        // Get raw Phenotype package name\n        String phenotypePackageName = mPackageListFiltered.get(position).getPhenotypePackageName();\n\n        // Initialize indexEnd\n        int indexEnd = 0;\n\n        // Try to split package name by dot character: look for the first 4 parts, if there aren't try 3, 2 and so on\n        for (int i = 4; i >= 0; i--) {\n            int indexEndTmp = StringUtils.ordinalIndexOf(phenotypePackageName, \".\", i);\n            if (indexEndTmp != -1) {\n                indexEnd = indexEndTmp;\n                break;\n            }\n        }\n\n        // Increment indexEnd by 2 to show two more characters beyond the found parts\n        if (indexEnd + 2 <= phenotypePackageName.length())\n            indexEnd += 2;\n        else\n            indexEnd = phenotypePackageName.length();\n\n        // Return the phenotypePackageName parsed substring\n        return phenotypePackageName.substring(0, indexEnd);\n    }\n\n    public static class ViewHolder extends RecyclerView.ViewHolder {\n        private final LinearLayout mRow;\n        private final ImageView mPackageIcon;\n        private final TextView mAppName;\n        private final TextView mPhenotypePackageName;\n\n        public ViewHolder(PackageRowBinding binding) {\n            super(binding.getRoot());\n            mRow = binding.row;\n            mPackageIcon = binding.packageIcon;\n            mAppName = binding.appName;\n            mPhenotypePackageName = binding.phenotypePackageName;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/fragment/BooleanModsFragment.java",
    "content": "package com.jacopomii.gappsmod.ui.fragment;\n\nimport static com.jacopomii.gappsmod.util.Utils.showSelectPackageDialog;\n\nimport android.app.Activity;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.core.view.MenuProvider;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.Lifecycle;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.FragmentBooleanModsBinding;\nimport com.jacopomii.gappsmod.ui.activity.MainActivity;\nimport com.jacopomii.gappsmod.ui.adapter.BooleanModsRecyclerViewAdapter;\nimport com.jacopomii.gappsmod.ui.view.FilterableSearchView;\nimport com.l4digital.fastscroll.FastScrollRecyclerView;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\npublic class BooleanModsFragment extends Fragment {\n    private FragmentBooleanModsBinding mBinding;\n\n    private BooleanModsRecyclerViewAdapter mFlagsRecyclerViewAdapter;\n\n    private ICoreRootService mCoreRootServiceIpc;\n\n    private String flagsFilterKey = \"\";\n    private boolean flagsFilterEnabled = true;\n    private boolean flagsFilterDisabled = true;\n    private boolean flagsFilterChanged = true;\n    private boolean flagsFilterUnchanged = true;\n\n    public BooleanModsFragment() {\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        Activity activity = getActivity();\n        if (activity instanceof MainActivity)\n            mCoreRootServiceIpc = ((MainActivity) activity).getCoreRootServiceIpc();\n        else\n            throw new RuntimeException(\"SuggestedModsFragment can be attached only to the MainActivity\");\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        // View bindings\n        mBinding = FragmentBooleanModsBinding.inflate(getLayoutInflater());\n\n\n        // Setup menu\n        setupMenu();\n\n\n        // Select package\n        TextView selectPackage = mBinding.selectPackage;\n\n        // Initialize the selectPackageDialogOpened\n        AtomicBoolean selectPackageDialogOpened = new AtomicBoolean(false);\n\n        // Select package onClick\n        selectPackage.setOnClickListener(v -> {\n            // Clear focus from other views\n            View currentFocus = requireActivity().getCurrentFocus();\n            if (currentFocus != null) currentFocus.clearFocus();\n\n            // If the select package dialog isn't already opened\n            if (!selectPackageDialogOpened.get()) {\n                // Set the selectPackageDialogOpened to true\n                selectPackageDialogOpened.set(true);\n\n                // Show the select package dialog\n                showSelectPackageDialog(getContext(), mCoreRootServiceIpc, item -> {\n                    // The item received by the listener here is the Phenotype package name chosen by the user\n\n                    // Update the select package textview\n                    selectPackage.setText((String) item);\n\n                    // Update the selectPackageRecyclerView adapter\n                    mFlagsRecyclerViewAdapter.selectPhenotypePackageName((String) item);\n\n                    // Set the selectPackageDialogOpened to false\n                    selectPackageDialogOpened.set(false);\n                }, dialog -> {\n                    // Set the selectPackageDialogOpened to false dismissing the dialog\n                    selectPackageDialogOpened.set(false);\n                });\n            }\n        });\n\n\n        // Flags recyclerview\n        FastScrollRecyclerView flagsRecyclerView = mBinding.recyclerview;\n\n        // Initialize the flagsRecyclerView adapter\n        mFlagsRecyclerViewAdapter = new BooleanModsRecyclerViewAdapter(getContext(), mCoreRootServiceIpc);\n\n        // Disable fast scroll if the flagsRecyclerView is empty or changes to empty\n        flagsRecyclerView.setFastScrollEnabled(mFlagsRecyclerViewAdapter.getItemCount() != 0);\n        mFlagsRecyclerViewAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n            @Override\n            public void onChanged() {\n                super.onChanged();\n                flagsRecyclerView.setFastScrollEnabled(mFlagsRecyclerViewAdapter.getItemCount() != 0);\n            }\n        });\n\n        // Set flagsRecyclerView items padding\n        flagsRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {\n            @Override\n            public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n                int padding = (int) getResources().getDimension(R.dimen.margin_generic);\n\n                int itemPosition = parent.getChildAdapterPosition(view);\n\n                if (itemPosition == 0) outRect.top = padding;\n                else if (itemPosition == mFlagsRecyclerViewAdapter.getItemCount() - 1)\n                    outRect.bottom = padding;\n\n                outRect.left = padding;\n                outRect.right = padding;\n            }\n        });\n\n        // Set the flagsRecyclerView LayoutManager and Adapter\n        flagsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));\n        flagsRecyclerView.setAdapter(mFlagsRecyclerViewAdapter);\n\n\n        // Return the fragment view\n        return mBinding.getRoot();\n    }\n\n    private void setupMenu() {\n        requireActivity().addMenuProvider(new MenuProvider() {\n            @Override\n            public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {\n                // Inflate the menu layout\n                menuInflater.inflate(R.menu.search_menu, menu);\n\n                // Initialize the menuSearchIcon\n                MenuItem menuSearchIcon = menu.findItem(R.id.menu_search_icon);\n\n                // Initialize filterableSearchView and the additional filter container\n                FilterableSearchView filterableSearchView = (FilterableSearchView) menuSearchIcon.getActionView();\n                filterableSearchView.setQueryHint(getString(R.string.search_by_flag));\n                filterableSearchView.setFilterContainer(mBinding.filterContainer, false);\n\n                // Initialize the filterEnabledStatusSpinner\n                String[] filterEnabledStatusSpinnerChoices = new String[]{getString(R.string.enabled_and_disabled), getString(R.string.enabled_only), getString(R.string.disabled_only)};\n                mBinding.filterEnabledStatusSpinner.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_dropdown_item, filterEnabledStatusSpinnerChoices));\n                mBinding.filterEnabledStatusSpinner.setOnItemClickListener((parent, view, position, id) -> {\n                    flagsFilterEnabled = position == 0 || position == 1;\n                    flagsFilterDisabled = position == 0 || position == 2;\n                    applyFlagsFilters();\n                });\n\n                // Initialize the filterChangedStatusSpinner\n                String[] filterChangedStatusSpinnerChoices = new String[]{getString(R.string.changed_and_unchanged), getString(R.string.changed_only), getString(R.string.unchanged_only)};\n                mBinding.filterChangedStatusSpinner.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_dropdown_item, filterChangedStatusSpinnerChoices));\n                mBinding.filterChangedStatusSpinner.setOnItemClickListener((parent, view, position, id) -> {\n                    flagsFilterChanged = position == 0 || position == 1;\n                    flagsFilterUnchanged = position == 0 || position == 2;\n                    applyFlagsFilters();\n                });\n\n                // Set flags filters to default values\n                resetFlagsFilters();\n\n                // Handle menuSearchIcon expand / collapse actions\n                menuSearchIcon.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {\n                    @Override\n                    public boolean onMenuItemActionExpand(MenuItem item) {\n                        return true;\n                    }\n\n                    @Override\n                    public boolean onMenuItemActionCollapse(MenuItem item) {\n                        // When the search view is collapsed, flag filters need to be reset and applied\n                        resetFlagsFilters();\n                        applyFlagsFilters();\n                        return true;\n                    }\n                });\n\n                // Handle filterableSearchView search query changes\n                filterableSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {\n                    @Override\n                    public boolean onQueryTextSubmit(String query) {\n                        return false;\n                    }\n\n                    @Override\n                    public boolean onQueryTextChange(String newText) {\n                        flagsFilterKey = newText;\n                        applyFlagsFilters();\n                        return false;\n                    }\n                });\n            }\n\n            @Override\n            public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {\n                return false;\n            }\n        }, getViewLifecycleOwner(), Lifecycle.State.RESUMED);\n    }\n\n    private void resetFlagsFilters() {\n        flagsFilterKey = \"\";\n        flagsFilterEnabled = true;\n        flagsFilterDisabled = true;\n        flagsFilterChanged = true;\n        flagsFilterUnchanged = true;\n        mBinding.filterEnabledStatusSpinner.setText(mBinding.filterEnabledStatusSpinner.getAdapter().getItem(0).toString(), false);\n        mBinding.filterChangedStatusSpinner.setText(mBinding.filterChangedStatusSpinner.getAdapter().getItem(0).toString(), false);\n    }\n\n    private void applyFlagsFilters() {\n        try {\n            JSONObject filterConfig = new JSONObject();\n\n            filterConfig.put(\"key\", flagsFilterKey);\n            filterConfig.put(\"enabled\", flagsFilterEnabled);\n            filterConfig.put(\"disabled\", flagsFilterDisabled);\n            filterConfig.put(\"changed\", flagsFilterChanged);\n            filterConfig.put(\"unchanged\", flagsFilterUnchanged);\n\n            mFlagsRecyclerViewAdapter.getFilter().filter(filterConfig.toString());\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/fragment/InformationFragment.java",
    "content": "package com.jacopomii.gappsmod.ui.fragment;\n\nimport android.os.Bundle;\nimport android.text.method.LinkMovementMethod;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.Fragment;\n\nimport com.jacopomii.gappsmod.databinding.FragmentInformationBinding;\n\npublic class InformationFragment extends Fragment {\n    FragmentInformationBinding mBinding;\n\n    public InformationFragment() {}\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        mBinding = FragmentInformationBinding.inflate(getLayoutInflater());\n\n        // Links aren't clickable workaround\n        mBinding.madeWithLoveByJacopoTediosi.setMovementMethod(LinkMovementMethod.getInstance());\n\n        return mBinding.getRoot();\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/fragment/RevertModsFragment.java",
    "content": "package com.jacopomii.gappsmod.ui.fragment;\n\nimport static com.jacopomii.gappsmod.data.Constants.DIALER_CALLRECORDINGPROMPT;\nimport static com.jacopomii.gappsmod.data.Constants.DIALER_PHENOTYPE_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.util.Utils.showSelectPackageDialog;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.os.RemoteException;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.Fragment;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.FragmentRevertModsBinding;\nimport com.jacopomii.gappsmod.ui.activity.MainActivity;\nimport com.topjohnwu.superuser.nio.ExtendedFile;\nimport com.topjohnwu.superuser.nio.FileSystemManager;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport es.dmoral.toasty.Toasty;\n\npublic class RevertModsFragment extends Fragment {\n    FragmentRevertModsBinding mBinding;\n\n    private ICoreRootService mCoreRootServiceIpc;\n    private FileSystemManager mCoreRootServiceFSManager;\n\n    public RevertModsFragment() {\n    }\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        Activity activity = getActivity();\n        if (activity instanceof MainActivity) {\n            mCoreRootServiceIpc = ((MainActivity) activity).getCoreRootServiceIpc();\n            mCoreRootServiceFSManager = ((MainActivity) activity).getCoreRootServiceFSManager();\n        } else {\n            throw new RuntimeException(\"RevertModsFragment can be attached only to the MainActivity\");\n        }\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        // View bindings\n        mBinding = FragmentRevertModsBinding.inflate(getLayoutInflater());\n\n\n        // Select package for revert mods for specific package\n        TextView selectPackage = mBinding.selectPackage;\n\n        // Initialize the selectPackageDialogOpened\n        AtomicBoolean selectPackageDialogOpened = new AtomicBoolean(false);\n\n        // Select package onClick\n        selectPackage.setOnClickListener(v -> {\n            // If the select package dialog isn't already opened\n            if (!selectPackageDialogOpened.get()) {\n                // Set the selectPackageDialogOpened to true\n                selectPackageDialogOpened.set(true);\n\n                // Show the select package dialog\n                showSelectPackageDialog(\n                        getContext(),\n                        mCoreRootServiceIpc,\n                        item -> {\n                            // The item received by the listener here is the Phenotype package name chosen by the user\n\n                            // Update the select package textview\n                            selectPackage.setText((String) item);\n\n                            // Enable the revertModsSelectedPackageButton\n                            mBinding.revertModsSelectedPackageButton.setEnabled(true);\n\n                        },\n                        dialog -> {\n                            // Set the selectPackageDialogOpened to false dismissing the dialog\n                            selectPackageDialogOpened.set(false);\n                        });\n            }\n        });\n\n\n        // Revert mods for the selected package button\n        mBinding.revertModsSelectedPackageButton.setOnClickListener(v -> {\n                    String selectedPhenotypePackageName = selectPackage.getText().toString();\n                    new MaterialAlertDialogBuilder(requireContext())\n                            .setMessage(String.format(getResources().getString(R.string.revert_mods_for_the_selected_package_confirm), selectedPhenotypePackageName))\n                            .setNegativeButton(getString(R.string.no), (dialog, which) -> {\n                            })\n                            .setPositiveButton(getString(R.string.yes), (dialog, which) -> {\n                                try {\n                                    // Delete all flag overrides for the selected package from Phenotype DB\n                                    mCoreRootServiceIpc.phenotypeDBDeleteAllFlagOverridesByPhenotypePackageName(selectedPhenotypePackageName);\n\n                                    // If the selected package was the Dialer\n                                    if (selectedPhenotypePackageName.equals(DIALER_PHENOTYPE_PACKAGE_NAME)) {\n                                        // Delete the com.google.android.dialer callrecordingprompt folder (if it exists)\n                                        ExtendedFile callRecordingPromptFolder = mCoreRootServiceFSManager.getFile(DIALER_CALLRECORDINGPROMPT);\n                                        if (callRecordingPromptFolder.exists()) {\n                                            //noinspection ResultOfMethodCallIgnored\n                                            callRecordingPromptFolder.delete();\n                                        }\n                                    }\n\n                                    // UI confirmation to the user\n                                    Toasty.success(requireContext(), getString(R.string.done), Toast.LENGTH_LONG, true).show();\n                                } catch (RemoteException e) {\n                                    e.printStackTrace();\n                                    Toasty.error(requireContext(), getString(R.string.an_error_has_occurred), Toast.LENGTH_LONG, true).show();\n                                }\n                            }).show();\n                }\n        );\n\n\n        // Revert all mods button\n        mBinding.revertAllModsButton.setOnClickListener(v ->\n                new MaterialAlertDialogBuilder(requireContext())\n                        .setMessage(R.string.revert_mods_for_all_packages_confirm)\n                        .setNegativeButton(getString(R.string.no), (dialog, which) -> {\n                        })\n                        .setPositiveButton(getString(R.string.yes), (dialog, which) -> {\n                            try {\n                                // Delete all flag overrides from Phenotype DB\n                                mCoreRootServiceIpc.phenotypeDBDeleteAllFlagOverrides();\n\n                                // Delete the com.google.android.dialer callrecordingprompt folder\n                                ExtendedFile callRecordingPromptFolder = mCoreRootServiceFSManager.getFile(DIALER_CALLRECORDINGPROMPT);\n                                if (callRecordingPromptFolder.exists()) {\n                                    //noinspection ResultOfMethodCallIgnored\n                                    callRecordingPromptFolder.delete();\n                                }\n\n                                // UI confirmation to the user\n                                Toasty.success(requireContext(), getString(R.string.done), Toast.LENGTH_LONG, true).show();\n                            } catch (RemoteException e) {\n                                e.printStackTrace();\n                                Toasty.error(requireContext(), getString(R.string.an_error_has_occurred), Toast.LENGTH_LONG, true).show();\n                            }\n                        }).show()\n        );\n\n        return mBinding.getRoot();\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/fragment/SuggestedModsFragment.java",
    "content": "package com.jacopomii.gappsmod.ui.fragment;\n\nimport static android.Manifest.permission.CAPTURE_AUDIO_OUTPUT;\nimport static com.jacopomii.gappsmod.data.Constants.DIALER_ANDROID_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.data.Constants.DIALER_CALLRECORDINGPROMPT;\nimport static com.jacopomii.gappsmod.data.Constants.DIALER_PHENOTYPE_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.data.Constants.GOOGLE_PLAY_BETA_LINK;\nimport static com.jacopomii.gappsmod.data.Constants.GOOGLE_PLAY_DETAILS_LINK;\nimport static com.jacopomii.gappsmod.data.Constants.MESSAGES_ANDROID_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.data.Constants.MESSAGES_PHENOTYPE_PACKAGE_NAME;\nimport static com.jacopomii.gappsmod.util.Utils.copyFile;\nimport static com.jacopomii.gappsmod.util.Utils.openGooglePlay;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.os.Bundle;\nimport android.os.RemoteException;\nimport android.telephony.TelephonyManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.Fragment;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.google.protobuf.ByteString;\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.FragmentSuggestedModsBinding;\nimport com.jacopomii.gappsmod.protos.Call_screen_i18n_config;\nimport com.jacopomii.gappsmod.ui.activity.MainActivity;\nimport com.jacopomii.gappsmod.ui.view.ProgrammaticMaterialSwitchView;\nimport com.topjohnwu.superuser.Shell;\nimport com.topjohnwu.superuser.nio.ExtendedFile;\nimport com.topjohnwu.superuser.nio.FileSystemManager;\n\nimport org.apache.commons.io.IOUtils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@SuppressWarnings(\"FieldCanBeLocal\")\npublic class SuggestedModsFragment extends Fragment {\n    private FragmentSuggestedModsBinding mBinding;\n\n    private ICoreRootService mCoreRootServiceIpc;\n    private FileSystemManager mCoreRootServiceFSManager;\n\n    // The following boolean flags force-enable Call Recording features in Dialer app\n    private final HashMap<String, Boolean> DIALER_ENABLE_CALL_RECORDING_FLAGS = new HashMap<String, Boolean>() {{\n        // Enable Call Recording feature\n        put(\"G__enable_call_recording\", true);\n        put(\"enable_call_recording_system_feature\", true);\n        // Enable Call Recording also for Google Fi / Fides (e2e calls, etc)\n        put(\"CallRecording__enable_call_recording_for_fi\", true);\n        // Bypass country-related restrictions for call recording feature\n        put(\"G__force_within_call_recording_geofence_value\", true);\n        // Bypass country-related restrictions for automatic call recording (\"always record\") feature\n        put(\"G__force_within_crosby_geofence_value\", true);\n        // Allow the usage of the above two \"force geofence\" flags\n        put(\"G__use_call_recording_geofence_overrides\", true);\n        // Show call recording button\n        put(\"enable_tidepods_call_recording\", true);\n    }};\n\n    // The following extensionVal flags concern the announcement audio played when a call recording starts or ends in Dialer app.\n    // To silence announcements, we set them as empty stringVal flags.\n    private final HashMap<String, String> DIALER_SILENCE_CALL_RECORDING_ALERTS_FLAGS = new HashMap<String, String>() {{\n        // The following flag contains a protobuf list of countries where the use of embedded audio is enforced.\n        // If its value is an empty string, the Dialer will by default use TTS to generate audio call recording alerts.\n        put(\"CallRecording__call_recording_countries_with_built_in_audio_file\", \"\");\n        // The following flags are no longer used in recent versions of the Dialer and remain here for backwards compatibility.\n        // They were used to contain a protobuf list of countries where the use of embedded or TTS audio was enforced.\n        put(\"CallRecording__call_recording_force_enable_built_in_audio_file_countries\", \"\");\n        put(\"CallRecording__call_recording_force_enable_tts_countries\", \"\");\n        // The following flag contains a protobuf hashset with country-language matches, used by Dialer to generate call recording audio alerts via TTS\n        // in the right language. If its value is an empty string, TTS will always fall back to en_US (hardcoded in the Dialer sources).\n        put(\"CallRecording__call_recording_countries\", \"\");\n    }};\n    private final String DIALER_CALLRECORDINGPROMPT_STARTING_VOICE_US = \"starting_voice-en_US.wav\";\n    private final String DIALER_CALLRECORDINGPROMPT_ENDING_VOICE_US = \"ending_voice-en_US.wav\";\n    // Dialer versionCode 10681248 (94.x) is the last version in which we can silence call recording alerts. In newer versions Google patched our hack.\n    private final int DIALER_SILENCE_CALL_RECORDING_ALERTS_MAX_VERSION = 10681248;\n\n    // The following boolean flags force-enable Call Screen / Revelio features in Dialer app\n    private final HashMap<String, Boolean> DIALER_ENABLE_CALL_SCREEN_FLAGS = new HashMap<String, Boolean>() {{\n        // Enable Call Screen feature for both calls and video-calls\n        put(\"G__speak_easy_enabled\", true);\n        put(\"enable_video_calling_screen\", true);\n        // Bypass Call Screen locale restrictions\n        put(\"G__speak_easy_bypass_locale_check\", true);\n        // Enable translations for additional locales\n        put(\"enable_call_screen_i18n_tidepods\", true);\n        // Enable the \"listen in\" button, which is located at the bottom right during screening\n        put(\"G__speak_easy_enable_listen_in_button\", true);\n        // Enable the Call Screen Demo page in Dialer settings\n        put(\"enable_call_screen_demo\", true);\n        // Enable the \"See transcript\" button in call history, which allows to read call screen transcripts and listen to recordings\n        put(\"G__enable_speakeasy_details\", true);\n        // Enable Revelio,an advanced version of the Call Screen which allows to automatically filter calls\n        put(\"G__enable_revelio\", true);\n        put(\"G__enable_revelio_on_bluetooth\", true);\n        put(\"G__enable_revelio_on_wired_headset\", true);\n        // Bypass Revelio locale restrictions\n        put(\"G__bypass_revelio_roaming_check\", true);\n        // Enable translations for additional locales also for Revelio\n        put(\"G__enable_tidepods_revelio\", true);\n        // Enable the Dialer settings option to save screened call audio (it does not depend on the Call Recording feature, but depends on Revelio)\n        put(\"G__enable_call_screen_saving_audio\", true);\n        // Enable the saving of the transcript also for Revelio\n        put(\"enable_revelio_transcript\", true);\n    }};\n\n    // The following extensionVal flag contains a protobuf (see call_screen_i18n.proto for its definition)\n    // which matches the languages to be used for the Call Screen feature to the supported countries in Dialer app\n    private final String DIALER_CALL_SCREEN_I18N_CONFIG_FLAG = \"CallScreenI18n__call_screen_i18n_config\";\n\n    // The following boolean flag force-enables debug menu in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_DEBUG_MENU_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__debug_menu_default_available\", true);\n    }};\n\n    // The following boolean flag force-enables marking conversations as unread in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_MARKING_CONVERSATIONS_UNREAD_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__enable_mark_as_unread\", true);\n    }};\n\n    // The following boolean flags force-enable Message Organization (Super Sort) features in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_MESSAGE_ORGANIZATION_FLAGS = new HashMap<String, Boolean>() {{\n        // Enable super sort\n        put(\"bugle_phenotype__conversation_labels_enabled\", true);\n        // Enable \"all\" category (this flag may be superfluous)\n        put(\"bugle_phenotype__supersort_badge_all_filter\", true);\n        // Enable donation banner\n        put(\"bugle_phenotype__supersort_enable_update_donation_banner\", true);\n        // Enable OTP auto deletion\n        put(\"bugle_phenotype__enable_otp_auto_deletion\", true);\n        // Classify messages also in the foreground (don't use only workmanager)\n        put(\"bugle_phenotype__supersort_use_only_work_manager\", false);\n        // I don't know what is it. In the Messages code I see that it's about \"generating annotations\" for \"money, coupon, account number, percentage\"\n        put(\"bugle_phenotype__enable_supersort_annotators\", true);\n        // QPBC = Participant Based Quick Classification. I don't know how it works.\n        put(\"bugle_phenotype__supersort_enable_qpbc\", true);\n    }};\n\n    // The following boolean flag force-enables verified SMS settings menu in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_VERIFIED_SMS_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__enabled_verified_sms\", true);\n    }};\n\n    // The following boolean flag force-enables sending images via GPhotos links in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_IMAGES_VIA_GPHOTOS_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__enable_google_photos_image_by_link\", true);\n    }};\n\n    // The following boolean flags force-enable nudges and birthday reminders in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_NUDGES_FLAGS = new HashMap<String, Boolean>() {{\n        // Enable nudges and birthday reminders\n        put(\"bugle_phenotype__enable_nudge\", true);\n        put(\"bugle_phenotype__enable_birthday_nudge\", true);\n        put(\"bugle_phenotype__enable_birthday_suggestions\", true);\n        // Enable banners\n        put(\"bugle_phenotype__enable_nudge_banner\", true);\n        put(\"bugle_phenotype__enable_birthday_banner\", true);\n        put(\"bugle_phenotype__enable_save_birthday_banner\", true);\n        // Enable settings pages\n        put(\"bugle_phenotype__enable_birthday_nudge_setting\", true);\n        put(\"bugle_phenotype__enable_birthday_banner_settings_button\", true);\n        // Leave the settings menu lines separate\n        put(\"bugle_phenotype__combing_nudge_settings\", false);\n    }};\n\n    // The following boolean flags force-enable spotlights suggestions settings menu in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_SPOTLIGHTS_FLAGS = new HashMap<String, Boolean>() {{\n        // Enable spotlights\n        put(\"bugle_phenotype__enable_spotlights\", true);\n        // Enable spotlights settings page\n        put(\"bugle_phenotype__enable_spotlight_settings_page\", true);\n        // Enable new settings page layout, otherwise it won't show up\n        put(\"bugle_phenotype__enable_smarts_settings_page_v2\", true);\n        // Enable additional spotlights features\n        put(\"bugle_phenotype__enable_spotlights_google_search\", true);\n    }};\n\n    // The following boolean flag force-enables smart compose (predictive writing) settings menu in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_SMART_COMPOSE_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__enable_smart_compose\", true);\n    }};\n\n    // The following boolean flags force-enable magic compose (draft suggestions with Bard AI) in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_MAGIC_COMPOSE_FLAGS = new HashMap<String, Boolean>() {{\n        // Enable magic compose view\n        put(\"bugle_phenotype__enable_magic_compose_view\", true);\n        // Idk what is it, but it has to be true to effectively enable magic compose\n        put(\"bugle_phenotype__enable_combined_magic_compose\", true);\n        // Enable all additional functionalities for magic compose (e.g., feedback and multiple writing styles)\n        put(\"bugle_phenotype__enable_additional_functionalities_for_magic_compose\", true);\n        // Enable magic compose also in xms\n        put(\"bugle_phenotype__magic_compose_enabled_in_xms\", true);\n    }};\n\n    // The following boolean flag force-enables smart actions (smart reply) in notifications in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_SMART_ACTIONS_IN_NOTIFICATIONS_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__enable_smart_actions_in_notifications\", true);\n    }};\n\n    // The following boolean flag force-enables suggested stickers settings menu in Messages app\n    private final HashMap<String, Boolean> MESSAGES_ENABLE_SUGGESTED_STICKERS_FLAGS = new HashMap<String, Boolean>() {{\n        put(\"bugle_phenotype__sticker_suggestions_setting_enabled\", true);\n    }};\n\n    public SuggestedModsFragment() {\n    }\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        Activity activity = getActivity();\n        if (activity instanceof MainActivity) {\n            mCoreRootServiceIpc = ((MainActivity) activity).getCoreRootServiceIpc();\n            mCoreRootServiceFSManager = ((MainActivity) activity).getCoreRootServiceFSManager();\n        } else {\n            throw new RuntimeException(\"SuggestedModsFragment can be attached only to the MainActivity\");\n        }\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        mBinding = FragmentSuggestedModsBinding.inflate(getLayoutInflater());\n\n        // GDialer\n        try {\n            // Beta and Install buttons actions\n            mBinding.dialerAppHeader.getBetaButton().setOnClickListener(v -> openGooglePlay(requireContext(), GOOGLE_PLAY_BETA_LINK + DIALER_ANDROID_PACKAGE_NAME));\n            mBinding.dialerAppHeader.getInstallButton().setOnClickListener(v -> openGooglePlay(requireContext(), GOOGLE_PLAY_DETAILS_LINK + DIALER_ANDROID_PACKAGE_NAME));\n\n            // Check if application is installed\n            requireContext().getPackageManager().getApplicationInfo(DIALER_ANDROID_PACKAGE_NAME, 0);\n\n            // Check if application has CAPTURE_AUDIO_OUTPUT permission\n            if (requireContext().getPackageManager().checkPermission(CAPTURE_AUDIO_OUTPUT, DIALER_ANDROID_PACKAGE_NAME) != PackageManager.PERMISSION_GRANTED)\n                mBinding.dialerPermissionAlert.setVisibility(View.VISIBLE);\n\n            // dialerForceEnableCallRecordingSwitch\n            ProgrammaticMaterialSwitchView dialerForceEnableCallRecordingSwitch = mBinding.dialerForceEnableCallRecording.getSwitch();\n            boolean dialerForceEnableCallRecordingSwitchChecked = modCheckAreAllFlagsOverridden(DIALER_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(DIALER_ENABLE_CALL_RECORDING_FLAGS.keySet()));\n            dialerForceEnableCallRecordingSwitch.setCheckedProgrammatically(dialerForceEnableCallRecordingSwitchChecked);\n            dialerForceEnableCallRecordingSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, DIALER_PHENOTYPE_PACKAGE_NAME, DIALER_ENABLE_CALL_RECORDING_FLAGS));\n            dialerForceEnableCallRecordingSwitch.setEnabled(true);\n\n            // dialerSilenceCallRecordingAlertsSwitch\n            ProgrammaticMaterialSwitchView dialerSilenceCallRecordingAlertsSwitch = mBinding.dialerSilenceCallRecordingAlerts.getSwitch();\n            boolean dialerSilenceCallRecordingAlertsSwitchChecked = false;\n            try {\n                ExtendedFile startingVoiceFile = mCoreRootServiceFSManager.getFile(DIALER_CALLRECORDINGPROMPT, DIALER_CALLRECORDINGPROMPT_STARTING_VOICE_US);\n                ExtendedFile endingVoiceFile = mCoreRootServiceFSManager.getFile(DIALER_CALLRECORDINGPROMPT, DIALER_CALLRECORDINGPROMPT_STARTING_VOICE_US);\n\n                if (startingVoiceFile.exists() && endingVoiceFile.exists()) {\n                    InputStream silentVoiceInputStream;\n\n                    InputStream startingVoiceInputStream = startingVoiceFile.newInputStream();\n                    silentVoiceInputStream = getResources().openRawResource(R.raw.silent_wav);\n                    boolean isStartingVoiceSilenced = IOUtils.contentEquals(silentVoiceInputStream, startingVoiceInputStream);\n                    startingVoiceInputStream.close();\n                    silentVoiceInputStream.close();\n\n                    InputStream endingVoiceInputStream = endingVoiceFile.newInputStream();\n                    silentVoiceInputStream = getResources().openRawResource(R.raw.silent_wav);\n                    boolean isEndingVoiceSilenced = IOUtils.contentEquals(silentVoiceInputStream, endingVoiceInputStream);\n                    endingVoiceInputStream.close();\n                    silentVoiceInputStream.close();\n\n                    dialerSilenceCallRecordingAlertsSwitchChecked = modCheckAreAllFlagsOverridden(DIALER_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(DIALER_SILENCE_CALL_RECORDING_ALERTS_FLAGS.keySet())) &&\n                            isStartingVoiceSilenced && isEndingVoiceSilenced;\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n            try {\n                // If Dialer version > SILENCE_CALL_RECORDING_ALERTS_MAX_VERSION the dialerSilenceCallRecordingAlertsSwitch must remain disabled\n                if (requireContext().getPackageManager().getPackageInfo(DIALER_ANDROID_PACKAGE_NAME, 0).versionCode > DIALER_SILENCE_CALL_RECORDING_ALERTS_MAX_VERSION) {\n                    // If the dialerSilenceCallRecordingAlertsSwitch was enabled in previous versions of GAppsMod, the silenceCallRecordingAlerts mod must be automatically disabled\n                    if (dialerSilenceCallRecordingAlertsSwitchChecked) {\n                        dialerSilenceCallRecordingAlerts(false);\n                    }\n                    // Otherwise, the dialerSilenceCallRecordingAlertsSwitch should be loaded as usual\n                } else {\n                    dialerSilenceCallRecordingAlertsSwitch.setCheckedProgrammatically(dialerSilenceCallRecordingAlertsSwitchChecked);\n                    dialerSilenceCallRecordingAlertsSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> dialerSilenceCallRecordingAlerts(isChecked));\n                    dialerSilenceCallRecordingAlertsSwitch.setEnabled(true);\n                }\n            } catch (PackageManager.NameNotFoundException e) {\n                e.printStackTrace();\n            }\n\n            // dialerForceEnableCallScreenSwitch\n            ProgrammaticMaterialSwitchView dialerForceEnableCallScreenSwitch = mBinding.dialerForceEnableCallScreen.getSwitch();\n            boolean dialerForceEnableCallScreenSwitchChecked = modCheckAreAllFlagsOverridden(DIALER_PHENOTYPE_PACKAGE_NAME, Collections.singletonList(DIALER_CALL_SCREEN_I18N_CONFIG_FLAG));\n            dialerForceEnableCallScreenSwitch.setCheckedProgrammatically(dialerForceEnableCallScreenSwitchChecked);\n            dialerForceEnableCallScreenSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> dialerForceEnableCallScreen(isChecked));\n            dialerForceEnableCallScreenSwitch.setEnabled(true);\n        } catch (PackageManager.NameNotFoundException e) {\n            mBinding.dialerNotInstalledAlert.setVisibility(View.VISIBLE);\n        }\n\n        // GMessages\n        try {\n            // Beta and Install buttons actions\n            mBinding.messagesAppHeader.getBetaButton().setOnClickListener(v -> openGooglePlay(requireContext(), GOOGLE_PLAY_BETA_LINK + MESSAGES_ANDROID_PACKAGE_NAME));\n            mBinding.messagesAppHeader.getInstallButton().setOnClickListener(v -> openGooglePlay(requireContext(), GOOGLE_PLAY_DETAILS_LINK + MESSAGES_ANDROID_PACKAGE_NAME));\n\n            // Check if application is installed\n            requireContext().getPackageManager().getApplicationInfo(MESSAGES_ANDROID_PACKAGE_NAME, 0);\n\n            // messagesForceEnableDebugMenuSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableDebugMenuSwitch = mBinding.messagesForceEnableDebugMenu.getSwitch();\n            boolean messagesForceEnableDebugMenuSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_DEBUG_MENU_FLAGS.keySet()));\n            messagesForceEnableDebugMenuSwitch.setCheckedProgrammatically(messagesForceEnableDebugMenuSwitchChecked);\n            messagesForceEnableDebugMenuSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_DEBUG_MENU_FLAGS));\n            messagesForceEnableDebugMenuSwitch.setEnabled(true);\n\n            // messagesForceEnableMarkingMessageThreadsUnreadSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableMarkingMessageThreadsUnreadSwitch = mBinding.messagesForceEnableMarkingMessageThreadsUnread.getSwitch();\n            boolean messagesForceEnableMarkingMessageThreadsUnreadSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_MARKING_CONVERSATIONS_UNREAD_FLAGS.keySet()));\n            messagesForceEnableMarkingMessageThreadsUnreadSwitch.setCheckedProgrammatically(messagesForceEnableMarkingMessageThreadsUnreadSwitchChecked);\n            messagesForceEnableMarkingMessageThreadsUnreadSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_MARKING_CONVERSATIONS_UNREAD_FLAGS));\n            messagesForceEnableMarkingMessageThreadsUnreadSwitch.setEnabled(true);\n\n            // messagesForceEnableMessageOrganizationSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableMessageOrganizationSwitch = mBinding.messagesForceEnableMessageOrganization.getSwitch();\n            boolean messagesForceEnableMessageOrganizationSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_MESSAGE_ORGANIZATION_FLAGS.keySet()));\n            messagesForceEnableMessageOrganizationSwitch.setCheckedProgrammatically(messagesForceEnableMessageOrganizationSwitchChecked);\n            messagesForceEnableMessageOrganizationSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_MESSAGE_ORGANIZATION_FLAGS));\n            messagesForceEnableMessageOrganizationSwitch.setEnabled(true);\n\n            // messagesForceEnableVerifiedSmsSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableVerifiedSmsSwitch = mBinding.messagesForceEnableVerifiedSms.getSwitch();\n            boolean messagesForceEnableVerifiedSmsSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_VERIFIED_SMS_FLAGS.keySet()));\n            messagesForceEnableVerifiedSmsSwitch.setCheckedProgrammatically(messagesForceEnableVerifiedSmsSwitchChecked);\n            messagesForceEnableVerifiedSmsSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_VERIFIED_SMS_FLAGS));\n            messagesForceEnableVerifiedSmsSwitch.setEnabled(true);\n\n            // messagesForceEnableGphotosSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableGphotosSwitch = mBinding.messagesForceEnableGphotos.getSwitch();\n            boolean messagesForceEnableGphotosSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_IMAGES_VIA_GPHOTOS_FLAGS.keySet()));\n            messagesForceEnableGphotosSwitch.setCheckedProgrammatically(messagesForceEnableGphotosSwitchChecked);\n            messagesForceEnableGphotosSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_IMAGES_VIA_GPHOTOS_FLAGS));\n            messagesForceEnableGphotosSwitch.setEnabled(true);\n\n            // messagesForceEnableNudgesSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableNudgesSwitch = mBinding.messagesForceEnableNudges.getSwitch();\n            boolean messagesForceEnableNudgesSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_NUDGES_FLAGS.keySet()));\n            messagesForceEnableNudgesSwitch.setCheckedProgrammatically(messagesForceEnableNudgesSwitchChecked);\n            messagesForceEnableNudgesSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_NUDGES_FLAGS));\n            messagesForceEnableNudgesSwitch.setEnabled(true);\n\n            // messagesForceEnableSpotlightsSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableSpotlightsSwitch = mBinding.messagesForceEnableSpotlights.getSwitch();\n            boolean messagesForceEnableSpotlightsSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_SPOTLIGHTS_FLAGS.keySet()));\n            messagesForceEnableSpotlightsSwitch.setCheckedProgrammatically(messagesForceEnableSpotlightsSwitchChecked);\n            messagesForceEnableSpotlightsSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_SPOTLIGHTS_FLAGS));\n            messagesForceEnableSpotlightsSwitch.setEnabled(true);\n\n            // messagesForceEnableSmartComposeSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableSmartComposeSwitch = mBinding.messagesForceEnableSmartCompose.getSwitch();\n            boolean messagesForceEnableSmartComposeSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_SMART_COMPOSE_FLAGS.keySet()));\n            messagesForceEnableSmartComposeSwitch.setCheckedProgrammatically(messagesForceEnableSmartComposeSwitchChecked);\n            messagesForceEnableSmartComposeSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_SMART_COMPOSE_FLAGS));\n            messagesForceEnableSmartComposeSwitch.setEnabled(true);\n\n            // messagesForceEnableMagicComposeSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableMagicComposeSwitch = mBinding.messagesForceEnableMagicCompose.getSwitch();\n            boolean messagesForceEnableMagicComposeSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_MAGIC_COMPOSE_FLAGS.keySet()));\n            messagesForceEnableMagicComposeSwitch.setCheckedProgrammatically(messagesForceEnableMagicComposeSwitchChecked);\n            messagesForceEnableMagicComposeSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_MAGIC_COMPOSE_FLAGS));\n            messagesForceEnableMagicComposeSwitch.setEnabled(true);\n\n            // messagesForceEnableSmartActionsInNotificationsSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableSmartActionsInNotificationsSwitch = mBinding.messagesForceEnableSmartActionsInNotifications.getSwitch();\n            boolean messagesForceEnableSmartActionsInNotificationsSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_SMART_ACTIONS_IN_NOTIFICATIONS_FLAGS.keySet()));\n            messagesForceEnableSmartActionsInNotificationsSwitch.setCheckedProgrammatically(messagesForceEnableSmartActionsInNotificationsSwitchChecked);\n            messagesForceEnableSmartActionsInNotificationsSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_SMART_ACTIONS_IN_NOTIFICATIONS_FLAGS));\n            messagesForceEnableSmartActionsInNotificationsSwitch.setEnabled(true);\n\n            // messagesForceEnableSuggestedStickersSwitch\n            ProgrammaticMaterialSwitchView messagesForceEnableSuggestedStickersSwitch = mBinding.messagesForceEnableSuggestedStickers.getSwitch();\n            boolean messagesForceEnableSuggestedStickersSwitchChecked = modCheckAreAllFlagsOverridden(MESSAGES_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(MESSAGES_ENABLE_SUGGESTED_STICKERS_FLAGS.keySet()));\n            messagesForceEnableSuggestedStickersSwitch.setCheckedProgrammatically(messagesForceEnableSuggestedStickersSwitchChecked);\n            messagesForceEnableSuggestedStickersSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> modSetBooleanFlags(isChecked, MESSAGES_PHENOTYPE_PACKAGE_NAME, MESSAGES_ENABLE_SUGGESTED_STICKERS_FLAGS));\n            messagesForceEnableSuggestedStickersSwitch.setEnabled(true);\n        } catch (PackageManager.NameNotFoundException e) {\n            mBinding.messagesNotInstalledAlert.setVisibility(View.VISIBLE);\n        }\n\n        return mBinding.getRoot();\n    }\n\n    private boolean modCheckAreAllFlagsOverridden(String phenotypePackageName, List<String> flags) {\n        try {\n            return mCoreRootServiceIpc.phenotypeDBAreAllFlagsOverridden(phenotypePackageName, flags);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    private void modSetBooleanFlags(boolean enableMod, String phenotypePackageName, HashMap<String, Boolean> flags) {\n        if (enableMod) {\n            for (Map.Entry<String, Boolean> flag : flags.entrySet()) {\n                try {\n                    mCoreRootServiceIpc.phenotypeDBOverrideBooleanFlag(phenotypePackageName, flag.getKey(), flag.getValue());\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n        } else {\n            modDeleteFlagOverrides(phenotypePackageName, new ArrayList<>(flags.keySet()));\n        }\n    }\n\n    private void modSetStringFlags(boolean enableMod, String phenotypePackageName, HashMap<String, String> flags) {\n        if (enableMod) {\n            for (Map.Entry<String, String> flag : flags.entrySet()) {\n                try {\n                    mCoreRootServiceIpc.phenotypeDBOverrideStringFlag(phenotypePackageName, flag.getKey(), flag.getValue());\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n        } else {\n            modDeleteFlagOverrides(phenotypePackageName, new ArrayList<>(flags.keySet()));\n        }\n    }\n\n    private void modDeleteFlagOverrides(String phenotypePackageName, List<String> flags) {\n        try {\n            mCoreRootServiceIpc.phenotypeDBDeleteFlagOverrides(phenotypePackageName, flags);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void dialerSilenceCallRecordingAlerts(boolean enableMod) {\n        // Set flags (or delete overrides)\n        modSetStringFlags(enableMod, DIALER_PHENOTYPE_PACKAGE_NAME, DIALER_SILENCE_CALL_RECORDING_ALERTS_FLAGS);\n\n        // Apply additional mods\n        if (enableMod) {\n            try {\n                // Create CALLRECORDINGPROMPT folder\n                ExtendedFile callRecordingPromptDir = mCoreRootServiceFSManager.getFile(DIALER_CALLRECORDINGPROMPT);\n                if (callRecordingPromptDir.mkdir() || (callRecordingPromptDir.exists() && callRecordingPromptDir.isDirectory())) {\n                    // Overwrite the two alert files with an empty audio\n                    ExtendedFile startingVoice = mCoreRootServiceFSManager.getFile(callRecordingPromptDir, DIALER_CALLRECORDINGPROMPT_STARTING_VOICE_US);\n                    ExtendedFile endingVoice = mCoreRootServiceFSManager.getFile(callRecordingPromptDir, DIALER_CALLRECORDINGPROMPT_ENDING_VOICE_US);\n                    copyFile(getResources().openRawResource(R.raw.silent_wav), startingVoice.newOutputStream());\n                    copyFile(getResources().openRawResource(R.raw.silent_wav), endingVoice.newOutputStream());\n\n                    // Set the right permissions to files and folders\n                    final int uid = requireActivity().getPackageManager().getApplicationInfo(DIALER_ANDROID_PACKAGE_NAME, 0).uid;\n                    Shell.cmd(\n                            String.format(\"chown -R %s:%s %s\", uid, uid, DIALER_CALLRECORDINGPROMPT),\n                            String.format(\"chmod 755 %s\", DIALER_CALLRECORDINGPROMPT),\n                            String.format(\"chmod 444 %s/*\", DIALER_CALLRECORDINGPROMPT),\n                            String.format(\"restorecon -R %s\", DIALER_CALLRECORDINGPROMPT)\n                    ).exec();\n                }\n            } catch (PackageManager.NameNotFoundException | IOException e) {\n                e.printStackTrace();\n            }\n        } else {\n            // Delete callrecordingprompt folder\n            ExtendedFile callRecordingPromptFolder = mCoreRootServiceFSManager.getFile(DIALER_CALLRECORDINGPROMPT);\n            if (callRecordingPromptFolder.exists()) {\n                //noinspection ResultOfMethodCallIgnored\n                callRecordingPromptFolder.delete();\n            }\n        }\n    }\n\n    private void dialerForceEnableCallScreen(boolean enableMod) {\n        if (enableMod) {\n            // Ask the user what language the Call Screen feature should use\n            String[] supportedLanguages = {\"en\", \"en-AU\", \"en-GB\", \"en-IN\", \"ja-JP\", \"fr-FR\", \"hi-IN\", \"de-DE\", \"it-IT\", \"es-ES\"};\n            final int[] chosenLanguageIndex = {0};\n            new MaterialAlertDialogBuilder(requireContext())\n                    .setTitle(R.string.choose_a_language_for_call_screen)\n                    .setSingleChoiceItems(supportedLanguages, chosenLanguageIndex[0], (dialog, which) -> chosenLanguageIndex[0] = which)\n                    .setPositiveButton(android.R.string.ok, (dialog, which) -> {\n                        // Update boolean flags\n                        modSetBooleanFlags(true, DIALER_PHENOTYPE_PACKAGE_NAME, DIALER_ENABLE_CALL_SCREEN_FLAGS);\n\n                        // Override the call screen i18n config extension flag with the user desired language\n                        TelephonyManager telephonyManager = (TelephonyManager) requireActivity().getSystemService(Context.TELEPHONY_SERVICE);\n                        String simCountryIso = telephonyManager.getSimCountryIso();\n\n                        String chosenLanguage = supportedLanguages[chosenLanguageIndex[0]];\n\n                        Call_screen_i18n_config call_screen_i18n_config = Call_screen_i18n_config.newBuilder()\n                                .addCountryConfigs(\n                                        Call_screen_i18n_config.CountryConfig.newBuilder()\n                                                .setCountry(simCountryIso)\n                                                .setLanguageConfig(\n                                                        Call_screen_i18n_config.LanguageConfig.newBuilder()\n                                                                .addLanguages(\n                                                                        Call_screen_i18n_config.Language.newBuilder()\n                                                                                .setLanguageCode(chosenLanguage)\n                                                                                .setA6(\n                                                                                        Call_screen_i18n_config.A6.newBuilder()\n                                                                                                .setA7(ByteString.copyFrom(new byte[]{2}))\n                                                                                )\n                                                                )\n                                                )\n                                ).build();\n                        try {\n                            mCoreRootServiceIpc.phenotypeDBOverrideExtensionFlag(DIALER_PHENOTYPE_PACKAGE_NAME, DIALER_CALL_SCREEN_I18N_CONFIG_FLAG, call_screen_i18n_config.toByteArray());\n                        } catch (RemoteException e) {\n                            e.printStackTrace();\n                        }\n                    })\n                    .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel())\n                    .setOnCancelListener(dialog -> mBinding.dialerForceEnableCallScreen.getSwitch().setCheckedProgrammatically(false))\n                    .show();\n        } else {\n            // Delete flag overrides\n            modDeleteFlagOverrides(DIALER_PHENOTYPE_PACKAGE_NAME, new ArrayList<>(DIALER_ENABLE_CALL_SCREEN_FLAGS.keySet()));\n            modDeleteFlagOverrides(DIALER_PHENOTYPE_PACKAGE_NAME, Collections.singletonList(DIALER_CALL_SCREEN_I18N_CONFIG_FLAG));\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/view/FilterableSearchView.java",
    "content": "package com.jacopomii.gappsmod.ui.view;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\nimport androidx.appcompat.view.CollapsibleActionView;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.core.content.res.ResourcesCompat;\n\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.FilterableSearchviewBinding;\n\n/**\n * A View, containing a {@link SearchView}, to which an additional View can optionally be connected\n * as a container to contain components for filtering the search, via the\n * {@link #setFilterContainer} method. When a filterContainer is set, a button appears next to the\n * SearchView that allows the user to manually show / hide the filterContainer.\n */\n// Here I use the deprecated CollapsibleActionView interface because otherwise the\n// onActionViewExpanded and onActionViewCollapsed methods are never called, idk why\n@SuppressWarnings(\"deprecation\")\npublic class FilterableSearchView extends LinearLayout implements CollapsibleActionView {\n    FilterableSearchviewBinding mBinding;\n    private final Context mContext;\n\n    private View mFilterContainer;\n    private boolean mIsFilterContainerVisible;\n    private boolean mFilterContainerAutoExpand;\n\n    public FilterableSearchView(Context context) {\n        super(context);\n        mContext = context;\n        init();\n    }\n\n    public FilterableSearchView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        mContext = context;\n        init();\n    }\n\n    private void init() {\n        mBinding = FilterableSearchviewBinding.inflate(LayoutInflater.from(mContext), this, true);\n        mBinding.collapseFilterContainerButton.setOnClickListener(v -> {\n            if (mFilterContainer != null) setFilterContainerVisibility(!mIsFilterContainerVisible);\n        });\n    }\n\n    /**\n     * Connects a filter container to the SearchView, which can be shown / hidden by the user\n     * using a special button.\n     *\n     * @param filterContainer           the filter container to attach.\n     * @param filterContainerAutoExpand whether the filter container should open itself when the\n     *                                  SearchView is expanded.\n     */\n    public void setFilterContainer(View filterContainer, boolean filterContainerAutoExpand) {\n        mFilterContainer = filterContainer;\n        mFilterContainerAutoExpand = filterContainerAutoExpand;\n        mBinding.collapseFilterContainerButton.setVisibility(VISIBLE);\n    }\n\n    /**\n     * Sets the hint text to display in the query text field of the SearchView.\n     *\n     * @param hint the hint text to display or {@code null}.\n     */\n    public void setQueryHint(CharSequence hint) {\n        mBinding.searchView.setQueryHint(hint);\n    }\n\n    /**\n     * Sets a listener for user actions within the SearchView.\n     *\n     * @param listener the listener object that receives callbacks when the user performs actions\n     *                 in the SearchView such as clicking on buttons or typing a query.\n     */\n    public void setOnQueryTextListener(SearchView.OnQueryTextListener listener) {\n        mBinding.searchView.setOnQueryTextListener(listener);\n    }\n\n    @Override\n    public void onActionViewExpanded() {\n        if (mFilterContainer != null && mFilterContainerAutoExpand)\n            setFilterContainerVisibility(true);\n        mBinding.searchView.onActionViewExpanded();\n    }\n\n    @Override\n    public void onActionViewCollapsed() {\n        if (mFilterContainer != null) setFilterContainerVisibility(false);\n        mBinding.searchView.onActionViewCollapsed();\n    }\n\n    private void setFilterContainerVisibility(boolean visible) {\n        mIsFilterContainerVisible = visible;\n\n        int newFilterContainerVisibility;\n        int newCollapseFilterButtonDrawableID;\n\n        if (visible) {\n            newFilterContainerVisibility = View.VISIBLE;\n            newCollapseFilterButtonDrawableID = R.drawable.ic_arrow_up_24;\n        } else {\n            newFilterContainerVisibility = View.GONE;\n            newCollapseFilterButtonDrawableID = R.drawable.ic_arrow_down_24;\n        }\n\n        mFilterContainer.setVisibility(newFilterContainerVisibility);\n\n        Drawable newCollapseFilterButtonDrawable = ResourcesCompat.getDrawable(getResources(), newCollapseFilterButtonDrawableID, null);\n        mBinding.collapseFilterContainerButton.setImageDrawable(newCollapseFilterButtonDrawable);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/view/ProgrammaticMaterialSwitchView.java",
    "content": "package com.jacopomii.gappsmod.ui.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.google.android.material.materialswitch.MaterialSwitch;\n\n/**\n * A {@link MaterialSwitch} that allows to programmatically set the checked / unchecked state\n * without triggering the onCheckedChangeListener.\n */\npublic class ProgrammaticMaterialSwitchView extends MaterialSwitch {\n    private OnCheckedChangeListener mOnCheckedChangeListener = null;\n\n    public ProgrammaticMaterialSwitchView(@NonNull Context context) {\n        super(context);\n    }\n\n    public ProgrammaticMaterialSwitchView(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public ProgrammaticMaterialSwitchView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {\n        mOnCheckedChangeListener = listener;\n        super.setOnCheckedChangeListener(listener);\n    }\n\n    /**\n     * Programmatically change the checked state of the switch without calling any\n     * onCheckedChangeListener. Please note that any previously set onCheckedChangeListener will be\n     * preserved, even if this method does not call it.\n     *\n     * @param checked {@code true} to check the switch, {@code false} to uncheck it.\n     */\n    public void setCheckedProgrammatically(boolean checked) {\n        super.setOnCheckedChangeListener(null);\n        super.setChecked(checked);\n        super.setOnCheckedChangeListener(mOnCheckedChangeListener);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/view/SuggestedModsAppHeaderView.java",
    "content": "package com.jacopomii.gappsmod.ui.view;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.Nullable;\n\nimport com.google.android.material.button.MaterialButton;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.SuggestedModsAppHeaderBinding;\n\n/**\n * The application name header used by the \"Suggested Mods\" fragment.\n * It includes a large title for the app name and two localized buttons \"Beta\" and \"Install\".\n */\npublic class SuggestedModsAppHeaderView extends LinearLayout {\n    final SuggestedModsAppHeaderBinding mBinding;\n\n    public SuggestedModsAppHeaderView(Context context) {\n        super(context);\n\n        mBinding = SuggestedModsAppHeaderBinding.inflate(LayoutInflater.from(context), this, true);\n    }\n\n    public SuggestedModsAppHeaderView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n\n        mBinding = SuggestedModsAppHeaderBinding.inflate(LayoutInflater.from(context), this, true);\n\n        final TypedArray xmlAttrs = context.obtainStyledAttributes(attrs, R.styleable.SuggestedModsAppHeaderView);\n        final String appName = xmlAttrs.getString(R.styleable.SuggestedModsAppHeaderView_app_name);\n        xmlAttrs.recycle();\n\n        mBinding.appName.setText(appName);\n    }\n\n    public MaterialButton getBetaButton() {\n        return mBinding.betaButton;\n    }\n\n    public MaterialButton getInstallButton() {\n        return mBinding.installButton;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/ui/view/SwitchCardView.java",
    "content": "package com.jacopomii.gappsmod.ui.view;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.Nullable;\n\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.databinding.SwitchCardBinding;\n\n/**\n * A card that contains a {@link android.widget.TextView} and a\n * {@link ProgrammaticMaterialSwitchView} on a single line.\n * The text will be rendered in a separate textview from the switch to prevent accidentally\n * clicking on the text from triggering the switch.\n */\npublic class SwitchCardView extends LinearLayout {\n    final SwitchCardBinding mBinding;\n\n    public SwitchCardView(Context context) {\n        super(context);\n\n        mBinding = SwitchCardBinding.inflate(LayoutInflater.from(context), this, true);\n    }\n\n    public SwitchCardView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n\n        mBinding = SwitchCardBinding.inflate(LayoutInflater.from(context), this, true);\n\n        final TypedArray xmlAttrs = context.obtainStyledAttributes(attrs, R.styleable.SwitchCardView);\n        final String text = xmlAttrs.getString(R.styleable.SwitchCardView_text);\n        final boolean enabled = xmlAttrs.getBoolean(R.styleable.SwitchCardView_enabled, true);\n        xmlAttrs.recycle();\n\n        mBinding.switchCardTextview.setText(text);\n        mBinding.switchCardSwitch.setEnabled(enabled);\n    }\n\n    public ProgrammaticMaterialSwitchView getSwitch() {\n        return mBinding.switchCardSwitch;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/util/OnItemClickListener.java",
    "content": "package com.jacopomii.gappsmod.util;\n\n/**\n * A generic interface to handle clicks on recyclerview rows.\n */\npublic interface OnItemClickListener {\n    void onItemClick(Object item);\n}"
  },
  {
    "path": "app/src/main/java/com/jacopomii/gappsmod/util/Utils.java",
    "content": "package com.jacopomii.gappsmod.util;\n\nimport static com.jacopomii.gappsmod.data.Constants.VENDING_ANDROID_PACKAGE_NAME;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.CheckBox;\n\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.recyclerview.widget.DividerItemDecoration;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.android.volley.Request;\nimport com.android.volley.RequestQueue;\nimport com.android.volley.toolbox.JsonObjectRequest;\nimport com.android.volley.toolbox.RequestFuture;\nimport com.android.volley.toolbox.Volley;\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.jacopomii.gappsmod.BuildConfig;\nimport com.jacopomii.gappsmod.ICoreRootService;\nimport com.jacopomii.gappsmod.R;\nimport com.jacopomii.gappsmod.data.Version;\nimport com.jacopomii.gappsmod.databinding.DialogSelectPackageBinding;\nimport com.jacopomii.gappsmod.ui.adapter.SelectPackageRecyclerViewAdapter;\nimport com.l4digital.fastscroll.FastScrollRecyclerView;\n\nimport org.json.JSONObject;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\npublic class Utils {\n    public static void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException {\n        byte[] buffer = new byte[1024];\n        int read;\n        while ((read = inputStream.read(buffer)) != -1) {\n            outputStream.write(buffer, 0, read);\n        }\n        inputStream.close();\n        outputStream.flush();\n        outputStream.close();\n    }\n\n    public static void openGooglePlay(Context context, String googlePlayLink) {\n        try {\n            Intent appStoreIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(googlePlayLink));\n            appStoreIntent.setPackage(VENDING_ANDROID_PACKAGE_NAME);\n            context.startActivity(appStoreIntent);\n        } catch (ActivityNotFoundException exception) {\n            context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(googlePlayLink)));\n        }\n    }\n\n    public static boolean checkUpdateAvailable(Context context) {\n        RequestQueue requestQueue = Volley.newRequestQueue(context);\n        RequestFuture<JSONObject> future = RequestFuture.newFuture();\n\n        requestQueue.add(\n                new JsonObjectRequest(\n                        Request.Method.GET,\n                        context.getString(R.string.github_api_link) + \"/releases/latest\",\n                        null,\n                        future,\n                        future\n                )\n        );\n\n        try {\n            JSONObject response = future.get();\n            Version actualVersion = new Version(BuildConfig.VERSION_NAME);\n            Version fetchedVersion = new Version(response.getString(\"tag_name\").substring(1));\n            if (actualVersion.compareTo(fetchedVersion) < 0)\n                return true;\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return false;\n    }\n\n    /**\n     * This method generates strings used for IN queries.\n     * It creates string containing \"?\" characters repeated {@code size} times and separated by \",\".\n     *\n     * @param size size of the items.\n     * @return IN query string of the form ?,?,?,?.\n     */\n    public static String createInQueryString(int size) {\n        StringBuilder stringBuilder = new StringBuilder();\n        String separator = \"\";\n        for (int i = 0; i < size; i++) {\n            stringBuilder.append(separator);\n            stringBuilder.append(\"?\");\n            separator = \",\";\n        }\n        return stringBuilder.toString();\n    }\n\n    /**\n     * This method returns, given an {@code androidPackageName}, the label of the corresponding\n     * application, or a localized string \"Unknown\" if the application is not installed.\n     *\n     * @param context            context.\n     * @param androidPackageName the Android package name of the application to get the label for.\n     * @return the application label if the application exists; The localized string\n     * {@link R.string#unknown} otherwise.\n     */\n    public static String getApplicationLabelOrUnknown(Context context, String androidPackageName) {\n        String applicationLabel = context.getString(R.string.unknown);\n\n        try {\n            PackageManager packageManager = context.getPackageManager();\n            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(androidPackageName, 0);\n            if (applicationInfo != null)\n                applicationLabel = (String) (packageManager.getApplicationLabel(applicationInfo));\n        } catch (PackageManager.NameNotFoundException ignored) {\n        }\n\n        return applicationLabel;\n    }\n\n    // Static variables for showSelectPackageDialog()\n    private static CharSequence lastPackageSearched = null;\n    private static Boolean lastPackageSearchedRemember = true;\n\n    /**\n     * Show the \"Select Package\" dialog, a custom view to select package names contained in the\n     * Phenotype DB with search and fastscroll features.\n     *\n     * @param context             context.\n     * @param coreRootServiceIpc  a {@code ICoreRootService} instance.\n     * @param onItemClickListener an implementation of the {@link OnItemClickListener} interface,\n     *                            to perform actions after the user has selected a package.\n     *                            The received item is a string containing the selected Phenotype\n     *                            (not Android) package name.\n     */\n    public static void showSelectPackageDialog(Context context, ICoreRootService coreRootServiceIpc, OnItemClickListener onItemClickListener, DialogInterface.OnDismissListener onDismissListener) {\n        // Dialog builder\n        MaterialAlertDialogBuilder selectPackageDialogBuilder = new MaterialAlertDialogBuilder(context);\n\n        // Inflate dialog layout\n        DialogSelectPackageBinding dialogSelectPackageBinding = DialogSelectPackageBinding.inflate(LayoutInflater.from(context));\n        selectPackageDialogBuilder.setView(dialogSelectPackageBinding.getRoot());\n\n        // Create dialog\n        AlertDialog selectPackageDialog = selectPackageDialogBuilder.create();\n\n        // Set dialog custom height and width\n        selectPackageDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n        selectPackageDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n\n        // Set dialog onDismissListener\n        selectPackageDialog.setOnDismissListener(onDismissListener);\n\n        // Dialog components\n        SearchView selectPackageSearchView = dialogSelectPackageBinding.searchview;\n        FastScrollRecyclerView selectPackageRecyclerView = dialogSelectPackageBinding.recyclerview;\n        CheckBox SelectPackageRememberCheckbox = dialogSelectPackageBinding.remembercheckbox;\n\n        // Initialize the dialog adapter\n        SelectPackageRecyclerViewAdapter selectPackageRecyclerViewAdapter = new SelectPackageRecyclerViewAdapter(context, coreRootServiceIpc, item -> {\n            // Pass the received item to the caller onItemClickListener\n            onItemClickListener.onItemClick(item);\n\n            // Dismiss dialog\n            selectPackageDialog.dismiss();\n        });\n\n        // Disable fast scroll if the selectPackageRecyclerView is empty or changes to empty\n        selectPackageRecyclerView.setFastScrollEnabled(selectPackageRecyclerViewAdapter.getItemCount() != 0);\n        selectPackageRecyclerViewAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n            @Override\n            public void onChanged() {\n                super.onChanged();\n                selectPackageRecyclerView.setFastScrollEnabled(selectPackageRecyclerViewAdapter.getItemCount() != 0);\n            }\n        });\n\n        // Set the dialog selectPackageRecyclerView LayoutManager and Adapter\n        selectPackageRecyclerView.setLayoutManager(new LinearLayoutManager(context));\n        selectPackageRecyclerView.setAdapter(selectPackageRecyclerViewAdapter);\n\n        // Add list dividers to the selectPackageRecyclerView\n        selectPackageRecyclerView.addItemDecoration(new DividerItemDecoration(selectPackageRecyclerView.getContext(), DividerItemDecoration.VERTICAL));\n\n        // Dialog filter\n        selectPackageSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {\n            @Override\n            public boolean onQueryTextSubmit(String query) {\n                return false;\n            }\n\n            @Override\n            public boolean onQueryTextChange(String newText) {\n                lastPackageSearched = newText;\n                selectPackageRecyclerViewAdapter.getFilter().filter(newText);\n                return false;\n            }\n        });\n\n        // Remember last package searched\n        SelectPackageRememberCheckbox.setChecked(lastPackageSearchedRemember);\n        SelectPackageRememberCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> {\n            lastPackageSearchedRemember = isChecked;\n            lastPackageSearched = selectPackageSearchView.getQuery();\n        });\n        if (lastPackageSearched != null && lastPackageSearchedRemember)\n            selectPackageSearchView.setQuery(lastPackageSearched, true);\n\n        // Show dialog\n        selectPackageDialog.show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/proto/call_screen_i18n.proto",
    "content": "syntax = \"proto3\";\r\n\r\npackage com.jacopomii.gappsmod.protos;\r\n\r\noption java_multiple_files = true;\r\noption java_package = \"com.jacopomii.gappsmod.protos\";\r\noption java_outer_classname = \"CallScreenI18nProtos\";\r\n\r\n/*\r\n  Reversed from Dialer. Hex 0a140a026974120e0a0c0a0569742d495412030a0102 corresponds to the following JSON.\r\n  {\r\n    \"countryConfigs\":[{\r\n      \"country\": \"it\",\r\n      \"languageConfig\": {\r\n        \"languages\":[{\r\n          \"languageCode\":\"it-IT\",\r\n          \"a6\":{\r\n            \"a7\":2\r\n          }\r\n        }]\r\n      }\r\n    }]\r\n  }\r\n */\r\n\r\nmessage Call_screen_i18n_config {\r\n  message A6 {\r\n    bytes a7 = 1;\r\n  }\r\n\r\n  message Language {\r\n    string languageCode = 1;\r\n    A6 a6 = 2;\r\n  }\r\n\r\n  message LanguageConfig {\r\n    repeated Language languages = 1;\r\n  }\r\n\r\n  message CountryConfig {\r\n    string country = 1;\r\n    LanguageConfig languageConfig = 2;\r\n  }\r\n\r\n  repeated CountryConfig countryConfigs = 1;\r\n}"
  },
  {
    "path": "app/src/main/res/drawable/ic_arrow_down_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_arrow_up_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_beta_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M19.8,18.4L14,10.67V6.5l1.35,-1.69C15.61,4.48 15.38,4 14.96,4H9.04C8.62,4 8.39,4.48 8.65,4.81L10,6.5v4.17L4.2,18.4C3.71,19.06 4.18,20 5,20h14C19.82,20 20.29,19.06 19.8,18.4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_error_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_fail_24.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\" android:tint=\"#f44336\" android:height=\"24dp\" android:width=\"24dp\" android:viewportWidth=\"24\" android:viewportHeight=\"24\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M18.3,5.71L18.3,5.71c-0.39,-0.39 -1.02,-0.39 -1.41,0L12,10.59L7.11,5.7c-0.39,-0.39 -1.02,-0.39 -1.41,0l0,0c-0.39,0.39 -0.39,1.02 0,1.41L10.59,12L5.7,16.89c-0.39,0.39 -0.39,1.02 0,1.41l0,0c0.39,0.39 1.02,0.39 1.41,0L12,13.41l4.89,4.89c0.39,0.39 1.02,0.39 1.41,0l0,0c0.39,-0.39 0.39,-1.02 0,-1.41L13.41,12l4.89,-4.89C18.68,6.73 18.68,6.09 18.3,5.71z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_install_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_menu_search_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_nav_drawer_boolean_mods_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M17,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h10c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5zM17,15c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_nav_drawer_delete_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_nav_drawer_information_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_nav_drawer_suggested_mods_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M11,21h-1l1,-7H7.5c-0.58,0 -0.57,-0.32 -0.38,-0.66 0.19,-0.34 0.05,-0.08 0.07,-0.12C8.48,10.94 10.42,7.54 13,3h1l-1,7h3.5c0.49,0 0.56,0.33 0.47,0.51l-0.07,0.15C12.96,17.55 11,21 11,21z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_save_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_success_24.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/holo_green_dark\"\n        android:pathData=\"M18,6.7l-8.48,8.48l-3.54,-3.54c-0.39,-0.39 -1.02,-0.39 -1.41,0l0,0c-0.39,0.39 -0.39,1.02 0,1.41l4.24,4.24c0.39,0.39 1.02,0.39 1.41,0l9.18,-9.18c0.39,-0.39 0.39,-1.03 -0.01,-1.42l0,0C19.02,6.31 18.39,6.31 18,6.7z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layouts/activities/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.drawerlayout.widget.DrawerLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/drawer_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    tools:openDrawer=\"start\">\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:fitsSystemWindows=\"true\"\n        tools:context=\".ui.activity.MainActivity\">\n\n        <com.google.android.material.appbar.AppBarLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:fitsSystemWindows=\"true\"\n            app:liftOnScroll=\"false\">\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\" />\n\n        </com.google.android.material.appbar.AppBarLayout>\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/nav_host_fragment\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:defaultNavHost=\"true\"\n            app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n            app:navGraph=\"@navigation/mobile_navigation\" />\n\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n\n    <com.google.android.material.navigation.NavigationView\n        android:id=\"@+id/nav_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"start\"\n        app:drawerLayoutCornerSize=\"0dp\"\n        app:headerLayout=\"@layout/nav_drawer_header\"\n        app:menu=\"@menu/nav_drawer\" />\n</androidx.drawerlayout.widget.DrawerLayout>"
  },
  {
    "path": "app/src/main/res/layouts/activities/layout/activity_splash_screen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:animateLayoutChanges=\"true\"\n    android:animationCache=\"true\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/l1\">\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"@dimen/l2\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\"\n        tools:ignore=\"UseCompoundDrawables\">\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"70dp\"\n            android:importantForAccessibility=\"no\"\n            app:srcCompat=\"@mipmap/ic_launcher\" />\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"@dimen/l1\"\n            android:fontFamily=\"sans-serif-condensed-medium\"\n            android:text=\"@string/app_name\"\n            android:textSize=\"34sp\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l_50\"\n            android:animateLayoutChanges=\"true\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/done_root\"\n                android:layout_width=\"32dp\"\n                android:layout_height=\"32dp\"\n                android:importantForAccessibility=\"no\"\n                android:visibility=\"gone\" />\n\n            <com.google.android.material.progressindicator.CircularProgressIndicator\n                android:id=\"@+id/circular_root\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:indeterminate=\"true\"\n                app:indicatorSize=\"24dp\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:layout_marginStart=\"@dimen/l_50\"\n                android:text=\"@string/splash_screen_root\"\n                android:textAppearance=\"?attr/textAppearanceBody1\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l_50\"\n            android:animateLayoutChanges=\"true\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/done_root_service\"\n                android:layout_width=\"32dp\"\n                android:layout_height=\"32dp\"\n                android:importantForAccessibility=\"no\"\n                android:visibility=\"gone\" />\n\n            <com.google.android.material.progressindicator.CircularProgressIndicator\n                android:id=\"@+id/circular_root_service\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:indeterminate=\"true\"\n                app:indicatorSize=\"24dp\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:layout_marginStart=\"@dimen/l_50\"\n                android:text=\"@string/splash_screen_root_service\"\n                android:textAppearance=\"?attr/textAppearanceBody1\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l_50\"\n            android:animateLayoutChanges=\"true\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/done_phenotype_gms\"\n                android:layout_width=\"32dp\"\n                android:layout_height=\"32dp\"\n                android:importantForAccessibility=\"no\"\n                android:visibility=\"gone\" />\n\n            <com.google.android.material.progressindicator.CircularProgressIndicator\n                android:id=\"@+id/circular_phenotype_gms\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:indeterminate=\"true\"\n                app:indicatorSize=\"24dp\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:layout_marginStart=\"@dimen/l_50\"\n                android:text=\"@string/splash_screen_phenotype_gms\"\n                android:textAppearance=\"?attr/textAppearanceBody1\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l_50\"\n            android:animateLayoutChanges=\"true\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/done_updates\"\n                android:layout_width=\"32dp\"\n                android:layout_height=\"32dp\"\n                android:importantForAccessibility=\"no\"\n                android:visibility=\"gone\" />\n\n            <com.google.android.material.progressindicator.CircularProgressIndicator\n                android:id=\"@+id/circular_updates\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:indeterminate=\"true\"\n                app:indicatorSize=\"24dp\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:layout_marginStart=\"@dimen/l_50\"\n                android:text=\"@string/splash_screen_updates\"\n                android:textAppearance=\"?attr/textAppearanceBody1\" />\n        </LinearLayout>\n\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layouts/dialogs/layout/dialog_select_package.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingHorizontal=\"@dimen/l_50\"\n    android:paddingVertical=\"@dimen/l1\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"@dimen/l_50\"\n        android:text=\"@string/select_package\"\n        android:textAlignment=\"viewStart\"\n        android:textAppearance=\"?attr/textAppearanceTitleLarge\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\">\n\n        <androidx.appcompat.widget.SearchView\n            android:id=\"@+id/searchview\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l_50\"\n            android:layout_weight=\"1\"\n            app:iconifiedByDefault=\"false\"\n            app:queryHint=\"@string/search_by_package_or_app\"\n            app:searchIcon=\"@null\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_vertical\">\n\n            <CheckBox\n                android:id=\"@+id/remembercheckbox\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"0dp\"\n                tools:ignore=\"TouchTargetSizeCheck\" />\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:contentDescription=\"@string/remember\"\n                app:srcCompat=\"@drawable/ic_save_24\"\n                app:tint=\"?attr/colorControlNormal\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.l4digital.fastscroll.FastScrollRecyclerView\n            android:id=\"@+id/recyclerview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:bubbleColor=\"?attr/colorSecondary\"\n            app:bubbleSize=\"small\"\n            app:bubbleTextColor=\"?attr/colorOnSecondary\"\n            app:bubbleTextSize=\"15sp\"\n            app:handleColor=\"?attr/colorSecondary\"\n            app:hideScrollbar=\"false\"\n            app:trackColor=\"?attr/colorSecondary\" />\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layouts/fragments/layout/fragment_boolean_mods.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\".ui.fragment.BooleanModsFragment\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:liftOnScroll=\"false\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:divider=\"?android:dividerHorizontal\"\n            android:orientation=\"vertical\"\n            android:showDividers=\"middle\">\n\n            <LinearLayout\n                android:id=\"@+id/filter_container\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\"\n                android:paddingHorizontal=\"@dimen/l1\"\n                android:paddingVertical=\"@dimen/l_50\"\n                android:visibility=\"gone\"\n                tools:visibility=\"visible\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginBottom=\"@dimen/l_50\"\n                    android:text=\"@string/additional_filters\" />\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:baselineAligned=\"false\"\n                    android:orientation=\"horizontal\">\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        style=\"@style/Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu\"\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginEnd=\"@dimen/l_50\"\n                        android:layout_weight=\"1\"\n                        android:hint=\"@string/enabled_status_filter\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/filter_enabled_status_spinner\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:ellipsize=\"end\"\n                            android:inputType=\"none\"\n                            android:singleLine=\"true\"\n                            tools:ignore=\"LabelFor,TextContrastCheck,VisualLintTextFieldSize\" />\n\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        style=\"@style/Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu\"\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginStart=\"@dimen/l_50\"\n                        android:layout_weight=\"1\"\n                        android:hint=\"@string/changed_status_filter\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/filter_changed_status_spinner\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:ellipsize=\"end\"\n                            android:inputType=\"none\"\n                            android:singleLine=\"true\"\n                            tools:ignore=\"LabelFor,TextContrastCheck,VisualLintTextFieldSize\" />\n\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                </LinearLayout>\n\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginVertical=\"@dimen/l_50\"\n                android:gravity=\"center_vertical\"\n                android:paddingHorizontal=\"@dimen/l1\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginEnd=\"@dimen/l_50\"\n                    android:text=\"@string/package_spinner_label\" />\n\n                <TextView\n                    android:id=\"@+id/select_package\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center_vertical\"\n                    android:hint=\"@string/select_package\"\n                    app:drawableEndCompat=\"@drawable/ic_arrow_down_24\"\n                    app:drawableTint=\"?attr/colorControlNormal\"\n                    tools:ignore=\"TextContrastCheck\" />\n\n            </LinearLayout>\n\n        </LinearLayout>\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.l4digital.fastscroll.FastScrollRecyclerView\n            android:id=\"@+id/recyclerview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:fitsSystemWindows=\"true\"\n            app:bubbleColor=\"?attr/colorSecondary\"\n            app:bubbleTextColor=\"?attr/colorOnSecondary\"\n            app:handleColor=\"?attr/colorSecondary\"\n            app:hideScrollbar=\"false\"\n            app:trackColor=\"?attr/colorSecondary\" />\n    </FrameLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layouts/fragments/layout/fragment_information.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\".ui.fragment.SuggestedModsFragment\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:padding=\"@dimen/margin_generic\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingBottom=\"@dimen/l1\"\n            android:text=\"@string/what_is_app_name\"\n            android:textAlignment=\"viewStart\"\n            android:textAppearance=\"?attr/textAppearanceTitleLarge\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:justificationMode=\"inter_word\"\n            android:paddingBottom=\"@dimen/l1\"\n            android:text=\"@string/what_is_it_explanation\"\n            android:textAlignment=\"viewStart\"\n            tools:ignore=\"VisualLintLongText\"\n            tools:targetApi=\"o\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingBottom=\"@dimen/l1\"\n            android:text=\"@string/how_it_works\"\n            android:textAlignment=\"viewStart\"\n            android:textAppearance=\"?attr/textAppearanceTitleLarge\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:justificationMode=\"inter_word\"\n            android:paddingBottom=\"@dimen/l1\"\n            android:text=\"@string/how_it_works_explanation\"\n            android:textAlignment=\"viewStart\"\n            tools:ignore=\"VisualLintLongText\"\n            tools:targetApi=\"o\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingBottom=\"@dimen/l1\"\n            android:text=\"@string/credits\"\n            android:textAlignment=\"viewStart\"\n            android:textAppearance=\"?attr/textAppearanceTitleLarge\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:justificationMode=\"inter_word\"\n            android:paddingBottom=\"@dimen/l1\"\n            android:text=\"@string/if_bug_found_open_github_issue\"\n            android:textAlignment=\"viewStart\"\n            tools:targetApi=\"o\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:autoLink=\"web\"\n            android:paddingBottom=\"@dimen/l_75\"\n            android:text=\"@string/github_link\"\n            android:textAlignment=\"viewStart\"\n            tools:ignore=\"TouchTargetSizeCheck\" />\n\n        <TextView\n            android:id=\"@+id/made_with_love_by_jacopo_tediosi\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/made_with_love_by\"\n            android:textAlignment=\"viewStart\" />\n\n    </LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layouts/fragments/layout/fragment_revert_mods.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\".ui.fragment.RevertModsFragment\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:padding=\"@dimen/margin_generic\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:justificationMode=\"inter_word\"\n            android:text=\"@string/revert_mods_for_the_selected_package_explanation\"\n            android:textAlignment=\"viewStart\"\n            tools:targetApi=\"o\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l1_5\"\n            android:gravity=\"center_vertical\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginEnd=\"@dimen/l_50\"\n                android:text=\"@string/package_spinner_label\" />\n\n            <TextView\n                android:id=\"@+id/select_package\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"center_vertical\"\n                android:hint=\"@string/select_package\"\n                app:drawableEndCompat=\"@drawable/ic_arrow_down_24\"\n                app:drawableTint=\"?attr/colorControlNormal\"\n                tools:ignore=\"TextContrastCheck\" />\n\n        </LinearLayout>\n\n        <Button\n            android:id=\"@+id/revert_mods_selected_package_button\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/l1_5\"\n            android:backgroundTint=\"?attr/colorError\"\n            android:enabled=\"false\"\n            android:text=\"@string/revert_mods_for_the_selected_package\"\n            android:textColor=\"?attr/colorOnError\"\n            tools:ignore=\"VisualLintButtonSize\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l2\"\n            android:text=\"@string/or\"\n            android:textAlignment=\"center\"\n            android:textAllCaps=\"true\"\n            android:textStyle=\"bold\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginVertical=\"@dimen/l1_5\"\n            android:justificationMode=\"inter_word\"\n            android:text=\"@string/revert_mods_for_all_packages_explanation\"\n            android:textAlignment=\"viewStart\"\n            tools:targetApi=\"o\" />\n\n        <Button\n            android:id=\"@+id/revert_all_mods_button\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:backgroundTint=\"?attr/colorError\"\n            android:text=\"@string/revert_mods_for_all_packages\"\n            android:textColor=\"?attr/colorOnError\"\n            tools:ignore=\"VisualLintButtonSize\" />\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layouts/fragments/layout/fragment_suggested_mods.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\".ui.fragment.SuggestedModsFragment\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:padding=\"@dimen/margin_generic\">\n\n        <com.jacopomii.gappsmod.ui.view.SuggestedModsAppHeaderView\n            android:id=\"@+id/dialer_app_header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:app_name=\"@string/dialer_app_name\" />\n\n        <com.google.android.material.card.MaterialCardView\n            android:id=\"@+id/dialer_not_installed_alert\"\n            style=\"?attr/materialCardViewElevatedStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/l_25\"\n            android:layout_marginBottom=\"@dimen/l_50\"\n            android:visibility=\"gone\"\n            app:cardBackgroundColor=\"?attr/colorErrorContainer\"\n            app:cardElevation=\"4dp\"\n            app:contentPadding=\"@dimen/l1\"\n            tools:visibility=\"visible\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:justificationMode=\"inter_word\"\n                android:text=\"@string/app_not_installed_error\"\n                android:textAlignment=\"viewStart\"\n                android:textColor=\"?attr/colorOnErrorContainer\"\n                tools:targetApi=\"o\" />\n        </com.google.android.material.card.MaterialCardView>\n\n        <com.google.android.material.card.MaterialCardView\n            android:id=\"@+id/dialer_permission_alert\"\n            style=\"?attr/materialCardViewElevatedStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/l_25\"\n            android:layout_marginBottom=\"@dimen/l_50\"\n            android:visibility=\"gone\"\n            app:cardBackgroundColor=\"?attr/colorErrorContainer\"\n            app:cardElevation=\"4dp\"\n            app:contentPadding=\"@dimen/l1\"\n            tools:visibility=\"visible\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:justificationMode=\"inter_word\"\n                android:text=\"@string/dialer_permission_alert\"\n                android:textAlignment=\"viewStart\"\n                android:textColor=\"?attr/colorOnErrorContainer\"\n                tools:targetApi=\"o\" />\n        </com.google.android.material.card.MaterialCardView>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/dialer_force_enable_call_recording\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_call_recording\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/dialer_silence_call_recording_alerts\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/silence_call_recording_alerts\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/dialer_force_enable_call_screen\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_call_screen\" />\n\n        </LinearLayout>\n\n        <com.jacopomii.gappsmod.ui.view.SuggestedModsAppHeaderView\n            android:id=\"@+id/messages_app_header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@dimen/l1\"\n            app:app_name=\"@string/messages_app_name\" />\n\n        <com.google.android.material.card.MaterialCardView\n            android:id=\"@+id/messages_not_installed_alert\"\n            style=\"?attr/materialCardViewElevatedStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/l_25\"\n            android:layout_marginBottom=\"@dimen/l_50\"\n            android:visibility=\"gone\"\n            app:cardBackgroundColor=\"?attr/colorErrorContainer\"\n            app:cardElevation=\"4dp\"\n            app:contentPadding=\"@dimen/l1\"\n            tools:visibility=\"visible\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:justificationMode=\"inter_word\"\n                    android:text=\"@string/app_not_installed_error\"\n                    android:textAlignment=\"viewStart\"\n                    android:textColor=\"?attr/colorOnErrorContainer\"\n                    tools:targetApi=\"o\" />\n            </LinearLayout>\n\n        </com.google.android.material.card.MaterialCardView>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_debug_menu\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_debug_menu\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_message_organization\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_message_organization\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_marking_message_threads_unread\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_marking_conversations_as_unread\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_verified_sms\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_verified_sms\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_gphotos\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_images_via_gphotos\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_nudges\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_nudges_and_birthdays\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_spotlights\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_spotlights\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_smart_compose\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_smart_compose_settings_menu\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_magic_compose\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_magic_compose\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_smart_actions_in_notifications\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_smart_actions_in_notifications\" />\n\n            <com.jacopomii.gappsmod.ui.view.SwitchCardView\n                android:id=\"@+id/messages_force_enable_suggested_stickers\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:enabled=\"false\"\n                app:text=\"@string/force_enable_suggested_stickers_settings_menu\" />\n        </LinearLayout>\n\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layouts/items/layout/filterable_searchview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"horizontal\">\n\n    <androidx.appcompat.widget.SearchView\n        android:id=\"@+id/search_view\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginVertical=\"@dimen/l_50\"\n        android:layout_weight=\"1\" />\n\n    <ImageView\n        android:id=\"@+id/collapse_filter_container_button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_marginHorizontal=\"@dimen/l1\"\n        android:contentDescription=\"@string/expand_additional_filters\"\n        android:src=\"@drawable/ic_arrow_down_24\"\n        android:visibility=\"gone\"\n        app:tint=\"?attr/colorControlNormal\"\n        tools:visibility=\"visible\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layouts/items/layout/nav_drawer_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginTop=\"@dimen/l2\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\"\n    android:paddingVertical=\"@dimen/l1_5\"\n    tools:ignore=\"UseCompoundDrawables\">\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"60dp\"\n        android:layout_marginBottom=\"@dimen/l_50\"\n        android:importantForAccessibility=\"no\"\n        app:srcCompat=\"@mipmap/ic_launcher\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:fontFamily=\"sans-serif-condensed-medium\"\n        android:text=\"@string/app_name\"\n        android:textSize=\"30sp\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layouts/items/layout/package_row.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/row\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?android:attr/selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:gravity=\"center_vertical\"\n    android:paddingVertical=\"@dimen/l_50\"\n    android:paddingStart=\"@dimen/l_50\"\n    android:paddingEnd=\"@dimen/l1\"\n    tools:ignore=\"Overdraw\">\n\n    <ImageView\n        android:id=\"@+id/package_icon\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginEnd=\"@dimen/l_50\"\n        android:importantForAccessibility=\"no\" />\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textStyle=\"bold\" />\n\n        <TextView\n            android:id=\"@+id/phenotype_package_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n    </LinearLayout>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layouts/items/layout/suggested_mods_app_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginBottom=\"@dimen/l_50\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <TextView\n        android:id=\"@+id/app_name\"\n        style=\"?attr/textAppearanceTitleLarge\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"@dimen/l_25\"\n        android:layout_marginEnd=\"@dimen/l_50\"\n        android:layout_weight=\"1\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/beta_button\"\n        style=\"?attr/materialIconButtonFilledTonalStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"@dimen/l_125\"\n        android:minHeight=\"0dp\"\n        android:paddingVertical=\"@dimen/l_50\"\n        android:singleLine=\"true\"\n        android:text=\"@string/beta\"\n        android:textSize=\"12sp\"\n        app:icon=\"@drawable/ic_beta_24\"\n        app:iconSize=\"@dimen/l1\"\n        tools:ignore=\"TouchTargetSizeCheck\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/install_button\"\n        style=\"?attr/materialIconButtonFilledTonalStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:minHeight=\"0dp\"\n        android:paddingVertical=\"@dimen/l_50\"\n        android:singleLine=\"true\"\n        android:text=\"@string/install\"\n        android:textSize=\"12sp\"\n        app:icon=\"@drawable/ic_install_24\"\n        app:iconSize=\"@dimen/l1\"\n        tools:ignore=\"TouchTargetSizeCheck\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layouts/items/layout/switch_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    style=\"?attr/materialCardViewElevatedStyle\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginVertical=\"@dimen/l_125\"\n    app:contentPaddingLeft=\"@dimen/l_75\"\n    app:contentPaddingRight=\"@dimen/l_75\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <TextView\n            android:id=\"@+id/switch_card_textview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginVertical=\"@dimen/l_50\"\n            android:layout_marginEnd=\"@dimen/l_50\"\n            android:layout_toStartOf=\"@id/switch_card_switch\"\n            android:labelFor=\"@id/switch_card_switch\"\n            android:textAlignment=\"viewStart\"\n            tools:ignore=\"LabelFor\" />\n\n        <com.jacopomii.gappsmod.ui.view.ProgrammaticMaterialSwitchView\n            android:id=\"@+id/switch_card_switch\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:saveEnabled=\"false\" />\n\n    </RelativeLayout>\n\n</com.google.android.material.card.MaterialCardView>"
  },
  {
    "path": "app/src/main/res/menu/nav_drawer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:showIn=\"navigation_view\">\n\n    <group\n        android:id=\"@+id/nav_drawer_first_group\"\n        android:checkableBehavior=\"single\">\n        <item\n            android:id=\"@+id/nav_suggested_mods\"\n            android:icon=\"@drawable/ic_nav_drawer_suggested_mods_24\"\n            android:title=\"@string/suggested_mods\" />\n        <item\n            android:id=\"@+id/nav_boolean_mods\"\n            android:icon=\"@drawable/ic_nav_drawer_boolean_mods_24\"\n            android:title=\"@string/boolean_mods\" />\n        <item\n            android:id=\"@+id/nav_revert_mods\"\n            android:icon=\"@drawable/ic_nav_drawer_delete_24\"\n            android:title=\"@string/revert_mods\" />\n    </group>\n\n    <group\n        android:id=\"@+id/nav_drawer_second_group\"\n        android:checkableBehavior=\"single\">\n        <item\n            android:id=\"@+id/nav_information\"\n            android:icon=\"@drawable/ic_nav_drawer_information_24\"\n            android:title=\"@string/information\" />\n    </group>\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/search_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/menu_search_icon\"\n        android:icon=\"@drawable/ic_menu_search_24\"\n        android:title=\"@string/search\"\n        app:actionViewClass=\"com.jacopomii.gappsmod.ui.view.FilterableSearchView\"\n        app:iconTint=\"?attr/colorControlNormal\"\n        app:showAsAction=\"ifRoom|collapseActionView\" />\n</menu>"
  },
  {
    "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=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n\n    <monochrome android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/navigation/mobile_navigation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/mobile_navigation\"\n    app:startDestination=\"@+id/nav_suggested_mods\">\n\n    <fragment\n        android:id=\"@+id/nav_suggested_mods\"\n        android:name=\"com.jacopomii.gappsmod.ui.fragment.SuggestedModsFragment\"\n        android:label=\"@string/suggested_mods\"\n        tools:layout=\"@layout/fragment_suggested_mods\" />\n\n    <fragment\n        android:id=\"@+id/nav_boolean_mods\"\n        android:name=\"com.jacopomii.gappsmod.ui.fragment.BooleanModsFragment\"\n        android:label=\"@string/boolean_mods\"\n        tools:layout=\"@layout/fragment_boolean_mods\" />\n\n    <fragment\n        android:id=\"@+id/nav_revert_mods\"\n        android:name=\"com.jacopomii.gappsmod.ui.fragment.RevertModsFragment\"\n        android:label=\"@string/revert_mods\"\n        tools:layout=\"@layout/fragment_revert_mods\" />\n\n    <fragment\n        android:id=\"@+id/nav_information\"\n        android:name=\"com.jacopomii.gappsmod.ui.fragment.InformationFragment\"\n        android:label=\"@string/information\"\n        tools:layout=\"@layout/fragment_information\" />\n</navigation>"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"SwitchCardView\">\n        <!-- The text accompanying the switch. -->\n        <attr name=\"text\" format=\"string\" />\n        <!-- Whether the switch should be enabled or not. Default true. -->\n        <attr name=\"enabled\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"SuggestedModsAppHeaderView\">\n        <!-- The app name. -->\n        <attr name=\"app_name\" format=\"string\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ViewStyle\">\n        <attr name=\"colorError\" format=\"color\" />\n        <attr name=\"colorSurface\" format=\"color\" />\n        <attr name=\"colorSecondaryContainer\" format=\"color\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Light theme colors -->\n    <color name=\"md_theme_light_primary\">#0060AA</color>\n    <color name=\"md_theme_light_onPrimary\">#FFFFFF</color>\n    <color name=\"md_theme_light_primaryContainer\">#D3E3FF</color>\n    <color name=\"md_theme_light_onPrimaryContainer\">#001C39</color>\n    <color name=\"md_theme_light_secondary\">#545F70</color>\n    <color name=\"md_theme_light_onSecondary\">#FFFFFF</color>\n    <color name=\"md_theme_light_secondaryContainer\">#D8E3F8</color>\n    <color name=\"md_theme_light_onSecondaryContainer\">#111C2B</color>\n    <color name=\"md_theme_light_tertiary\">#6D5677</color>\n    <color name=\"md_theme_light_onTertiary\">#FFFFFF</color>\n    <color name=\"md_theme_light_tertiaryContainer\">#F5D9FF</color>\n    <color name=\"md_theme_light_onTertiaryContainer\">#261430</color>\n    <color name=\"md_theme_light_error\">#BA1A1A</color>\n    <color name=\"md_theme_light_errorContainer\">#FFDAD6</color>\n    <color name=\"md_theme_light_onError\">#FFFFFF</color>\n    <color name=\"md_theme_light_onErrorContainer\">#410002</color>\n    <color name=\"md_theme_light_background\">#FDFCFF</color>\n    <color name=\"md_theme_light_onBackground\">#1A1C1E</color>\n    <color name=\"md_theme_light_surface\">#FDFCFF</color>\n    <color name=\"md_theme_light_onSurface\">#1A1C1E</color>\n    <color name=\"md_theme_light_surfaceVariant\">#DFE2EB</color>\n    <color name=\"md_theme_light_onSurfaceVariant\">#43474E</color>\n    <color name=\"md_theme_light_outline\">#73777F</color>\n    <color name=\"md_theme_light_inverseOnSurface\">#F1F0F4</color>\n    <color name=\"md_theme_light_inverseSurface\">#2F3033</color>\n    <color name=\"md_theme_light_inversePrimary\">#A3C9FF</color>\n\n    <!-- Dark theme colors -->\n    <color name=\"md_theme_dark_primary\">#A3C9FF</color>\n    <color name=\"md_theme_dark_onPrimary\">#00315C</color>\n    <color name=\"md_theme_dark_primaryContainer\">#004882</color>\n    <color name=\"md_theme_dark_onPrimaryContainer\">#D3E3FF</color>\n    <color name=\"md_theme_dark_secondary\">#BCC7DB</color>\n    <color name=\"md_theme_dark_onSecondary\">#263141</color>\n    <color name=\"md_theme_dark_secondaryContainer\">#3C4758</color>\n    <color name=\"md_theme_dark_onSecondaryContainer\">#D8E3F8</color>\n    <color name=\"md_theme_dark_tertiary\">#D9BDE3</color>\n    <color name=\"md_theme_dark_onTertiary\">#3C2947</color>\n    <color name=\"md_theme_dark_tertiaryContainer\">#543F5E</color>\n    <color name=\"md_theme_dark_onTertiaryContainer\">#F5D9FF</color>\n    <color name=\"md_theme_dark_error\">#FFB4AB</color>\n    <color name=\"md_theme_dark_errorContainer\">#93000A</color>\n    <color name=\"md_theme_dark_onError\">#690005</color>\n    <color name=\"md_theme_dark_onErrorContainer\">#FFDAD6</color>\n    <color name=\"md_theme_dark_background\">#1A1C1E</color>\n    <color name=\"md_theme_dark_onBackground\">#E3E2E6</color>\n    <color name=\"md_theme_dark_surface\">#1A1C1E</color>\n    <color name=\"md_theme_dark_onSurface\">#E3E2E6</color>\n    <color name=\"md_theme_dark_surfaceVariant\">#43474E</color>\n    <color name=\"md_theme_dark_onSurfaceVariant\">#C3C6CF</color>\n    <color name=\"md_theme_dark_outline\">#8D9199</color>\n    <color name=\"md_theme_dark_inverseOnSurface\">#1A1C1E</color>\n    <color name=\"md_theme_dark_inverseSurface\">#E3E2E6</color>\n    <color name=\"md_theme_dark_inversePrimary\">#0060AA</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"margin_generic\">16dp</dimen>\n\n    <dimen name=\"l_125\">2dp</dimen>\n    <dimen name=\"l_25\">4dp</dimen>\n    <dimen name=\"l_50\">8dp</dimen>\n    <dimen name=\"l_75\">12dp</dimen>\n    <dimen name=\"l1\">16dp</dimen>\n    <dimen name=\"l1_5\">24dp</dimen>\n    <dimen name=\"l2\">32dp</dimen>\n\n    <dimen name=\"fastscroll_scrollbar_padding_end\">4dp</dimen>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- App name -->\n    <string name=\"app_name\" translatable=\"false\">GAppsMod</string>\n\n    <!-- Github and links -->\n    <string name=\"github\" translatable=\"false\">Github</string>\n    <string name=\"github_api_link\" translatable=\"false\">https://api.github.com/repos/jacopotediosi/GAppsMod</string>\n    <string name=\"github_link\" translatable=\"false\">https://github.com/jacopotediosi/GAppsMod</string>\n\n    <!-- GApp names, taken from https://play.google.com/store/apps/details?id=PACKAGE_NAME&hl=en -->\n    <string name=\"dialer_app_name\">Phone by Google</string>\n    <string name=\"messages_app_name\">Messages by Google</string>\n    <string name=\"photos_app_name\" tools:keep=\"@string/photos_app_name\">Google Photos</string>\n\n    <!-- Common -->\n    <string name=\"beta\" translatable=\"false\">Beta</string>\n    <string name=\"continue_anyway\">Continue anyway</string>\n    <string name=\"done\">Done</string>\n    <string name=\"exit\">Exit</string>\n    <string name=\"install\">Install</string>\n    <string name=\"no\">No</string>\n    <string name=\"or\">Or</string>\n    <string name=\"yes\">Yes</string>\n    <string name=\"package_spinner_label\" translatable=\"false\">Package:</string>\n    <string name=\"remember\">Remember</string>\n    <string name=\"select_package\">Select package</string>\n    <string name=\"unknown\">Unknown</string>\n\n    <!-- Search and search filters -->\n    <string name=\"additional_filters\">Additional filters:</string>\n    <string name=\"changed_and_unchanged\">Changed and unchanged</string>\n    <string name=\"changed_only\">Changed only</string>\n    <string name=\"changed_status_filter\">Changed status filter</string>\n    <string name=\"disabled_only\">Disabled only</string>\n    <string name=\"enabled_and_disabled\">Enabled and disabled</string>\n    <string name=\"enabled_only\">Enabled only</string>\n    <string name=\"enabled_status_filter\">Enabled status filter</string>\n    <string name=\"expand_additional_filters\">Expand additional filters</string>\n    <string name=\"search\">Search</string>\n    <string name=\"search_by_flag\">Search by flag</string>\n    <string name=\"search_by_package_or_app\">Search by package or application</string>\n    <string name=\"unchanged_only\">Unchanged only</string>\n\n    <!-- Fragment titles -->\n    <string name=\"boolean_mods\">Boolean mods</string>\n    <string name=\"information\">Information</string>\n    <string name=\"revert_mods\">Revert mods</string>\n    <string name=\"suggested_mods\">Suggested mods</string>\n\n    <!-- Splash screen -->\n    <string name=\"splash_screen_phenotype_gms\">Checking Phenotype DB…</string>\n    <string name=\"splash_screen_root\">Checking root permissions…</string>\n    <string name=\"splash_screen_root_service\">Connecting to the root service…</string>\n    <string name=\"splash_screen_updates\">Checking updates…</string>\n\n    <!-- Suggested mods -->\n    <!-- Dialer -->\n    <string name=\"choose_a_language_for_call_screen\">Choose a language for call screen</string>\n    <string name=\"force_enable_call_recording\">Force enable call recording</string>\n    <string name=\"force_enable_call_screen\">Force enable call screen</string>\n    <string name=\"silence_call_recording_alerts\">Silence call recording alerts (${dialer_app_name} &lt;= 94.x only)</string>\n    <!-- Messages -->\n    <string name=\"force_enable_debug_menu\">Force enable debug menu</string>\n    <string name=\"force_enable_images_via_gphotos\">Force enable sending images via ${photos_app_name}</string>\n    <string name=\"force_enable_magic_compose\">Force enable magic compose (draft suggestions with Bard AI)</string>\n    <string name=\"force_enable_marking_conversations_as_unread\">Force enable marking conversations as unread</string>\n    <string name=\"force_enable_message_organization\">Force enable message organization (supersort)</string>\n    <string name=\"force_enable_nudges_and_birthdays\">Force enable nudges and birthday reminders</string>\n    <string name=\"force_enable_smart_actions_in_notifications\">Force enable smart actions (smart reply) in notifications</string>\n    <string name=\"force_enable_smart_compose_settings_menu\">Force enable smart compose settings menu</string>\n    <string name=\"force_enable_spotlights\">Force enable spotlights suggestions settings menu</string>\n    <string name=\"force_enable_suggested_stickers_settings_menu\">Force enable suggested stickers settings menu</string>\n    <string name=\"force_enable_verified_sms\">Force enable verified SMS settings menu</string>\n\n    <!-- Revert mods -->\n    <string name=\"revert_mods_for_all_packages\">Revert mods for all packages</string>\n    <string name=\"revert_mods_for_all_packages_confirm\">Do you really want to revert all mods?</string>\n    <string name=\"revert_mods_for_all_packages_explanation\">Press the button below to revert all mods applied to all packages.</string>\n    <string name=\"revert_mods_for_the_selected_package\">Revert mods for the selected package</string>\n    <string name=\"revert_mods_for_the_selected_package_confirm\">Do you really want to revert mods for the package %1$s?</string>\n    <string name=\"revert_mods_for_the_selected_package_explanation\">Select a package and press the button below to revert mods applied to that specific package.</string>\n\n    <!-- Information -->\n    <string name=\"credits\">Credits</string>\n    <string name=\"how_it_works\">How it works</string>\n    <string name=\"how_it_works_explanation\">The patches applied by ${app_name} survive Google Apps updates over time, even after uninstalling and reinstalling them.\\nMods are therefore to be considered permanent, but they can be easily reverted from the \\\"${revert_mods}\\\" section of the sidebar menu.\\nThere is no need to keep this app installed after applying the desired mods.\\n\\nAfter applying changes, you may have to force close and reopen the Google app you are trying to mod a few times before they take effect.</string>\n    <string name=\"if_bug_found_open_github_issue\">If you think you\\'ve found any bugs please open an issue on Github:</string>\n    <string name=\"made_with_love_by\">Made with ❤ by <a href=\"https://github.com/jacopotediosi\">Jacopo Tediosi</a> (and other amazing contributors)</string>\n    <string name=\"what_is_app_name\">What is ${app_name}</string>\n    <string name=\"what_is_it_explanation\">${app_name} is an app that allows, using root privileges, to modify the values contained in \"Phenotype\" database of Google Play Services, thus altering the behavior of Google Apps (such as ${dialer_app_name} and ${messages_app_name}) to unlock experimental features or features that are only available in certain countries or devices.</string>\n\n    <!-- Warning and error messages -->\n    <string name=\"an_error_has_occurred\">An error has occurred</string>\n    <string name=\"app_not_installed_error\">It looks like the app is not installed.</string>\n    <string name=\"dialer_permission_alert\">${dialer_app_name} is not installed as a system app and therefore does not have the CAPTURE_AUDIO_OUTPUT permission.\\nSome features, such as call recording, may not work.</string>\n    <string name=\"new_version_alert\">A new version of ${app_name} is available.\\n\\nYou can download it from Github Releases page.</string>\n    <string name=\"phenotype_db_does_not_exist_gms\">Phenotype DB does not appear to exist.\\n\\nInstall Google Play Services from Google Play and try again.</string>\n    <string name=\"root_access_denied\">Root access denied. Please allow root access.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Application light theme -->\n    <style name=\"AppTheme\" parent=\"BaseTheme\" />\n\n    <!-- Application light base theme -->\n    <style name=\"BaseTheme\" parent=\"Theme.Material3.Light.NoActionBar\">\n        <item name=\"colorPrimary\">@color/md_theme_light_primary</item>\n        <item name=\"colorOnPrimary\">@color/md_theme_light_onPrimary</item>\n        <item name=\"colorPrimaryContainer\">@color/md_theme_light_primaryContainer</item>\n        <item name=\"colorOnPrimaryContainer\">@color/md_theme_light_onPrimaryContainer</item>\n        <item name=\"colorSecondary\">@color/md_theme_light_secondary</item>\n        <item name=\"colorOnSecondary\">@color/md_theme_light_onSecondary</item>\n        <item name=\"colorSecondaryContainer\">@color/md_theme_light_secondaryContainer</item>\n        <item name=\"colorOnSecondaryContainer\">@color/md_theme_light_onSecondaryContainer</item>\n        <item name=\"colorTertiary\">@color/md_theme_light_tertiary</item>\n        <item name=\"colorOnTertiary\">@color/md_theme_light_onTertiary</item>\n        <item name=\"colorTertiaryContainer\">@color/md_theme_light_tertiaryContainer</item>\n        <item name=\"colorOnTertiaryContainer\">@color/md_theme_light_onTertiaryContainer</item>\n        <item name=\"colorError\">@color/md_theme_light_error</item>\n        <item name=\"colorErrorContainer\">@color/md_theme_light_errorContainer</item>\n        <item name=\"colorOnError\">@color/md_theme_light_onError</item>\n        <item name=\"colorOnErrorContainer\">@color/md_theme_light_onErrorContainer</item>\n        <item name=\"android:colorBackground\">@color/md_theme_light_background</item>\n        <item name=\"colorOnBackground\">@color/md_theme_light_onBackground</item>\n        <item name=\"colorSurface\">@color/md_theme_light_surface</item>\n        <item name=\"colorOnSurface\">@color/md_theme_light_onSurface</item>\n        <item name=\"colorSurfaceVariant\">@color/md_theme_light_surfaceVariant</item>\n        <item name=\"colorOnSurfaceVariant\">@color/md_theme_light_onSurfaceVariant</item>\n        <item name=\"colorOutline\">@color/md_theme_light_outline</item>\n        <item name=\"colorOnSurfaceInverse\">@color/md_theme_light_inverseOnSurface</item>\n        <item name=\"colorSurfaceInverse\">@color/md_theme_light_inverseSurface</item>\n        <item name=\"colorPrimaryInverse\">@color/md_theme_light_inversePrimary</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- GApp names, taken from https://play.google.com/store/apps/details?id=PACKAGE_NAME&hl=es -->\n    <string name=\"dialer_app_name\">Teléfono de Google</string>\n    <string name=\"messages_app_name\">Mensajes de Google</string>\n    <string name=\"photos_app_name\" tools:keep=\"@string/photos_app_name\">Google Fotos</string>\n\n    <!-- Common -->\n    <string name=\"continue_anyway\">Continuar de todos modos</string>\n    <string name=\"done\">Hecho</string>\n    <string name=\"exit\">Salir</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"no\">No</string>\n    <string name=\"or\">O</string>\n    <string name=\"yes\">Sí</string>\n    <string name=\"remember\">Recordar</string>\n    <string name=\"select_package\">Seleccione un package</string>\n    <string name=\"unknown\">Ignoto</string>\n\n    <!-- Search and search filters -->\n    <string name=\"additional_filters\">Filtros adicionales:</string>\n    <string name=\"changed_and_unchanged\">Modificado y no modificado</string>\n    <string name=\"changed_only\">Solo cambiado</string>\n    <string name=\"changed_status_filter\">Filtrar por modificados</string>\n    <string name=\"disabled_only\">Solo deshabilitado</string>\n    <string name=\"enabled_and_disabled\">Habilitado y deshabilitado</string>\n    <string name=\"enabled_only\">Solo habilitado</string>\n    <string name=\"enabled_status_filter\">Filtrar por estado</string>\n    <string name=\"expand_additional_filters\">Expandir filtros adicionales</string>\n    <string name=\"search\">Buscar</string>\n    <string name=\"search_by_flag\">Buscar por indicador</string>\n    <string name=\"search_by_package_or_app\">Buscar por paquete o aplicación</string>\n    <string name=\"unchanged_only\">Solo sin modificar</string>\n\n\n    <!-- Fragment titles -->\n    <string name=\"boolean_mods\">Mods booleanos</string>\n    <string name=\"information\">Información</string>\n    <string name=\"revert_mods\">Revertir mods</string>\n    <string name=\"suggested_mods\">Mods sugeridas</string>\n\n    <!-- Splash screen -->\n    <string name=\"splash_screen_phenotype_gms\">Comprobando Phenotype DB…</string>\n    <string name=\"splash_screen_root\">Comprobando permisos root…</string>\n    <string name=\"splash_screen_root_service\">Conexión al servicio root…</string>\n    <string name=\"splash_screen_updates\">Comprobando actualizaciones…</string>\n\n    <!-- Suggested mods -->\n    <!-- Dialer -->\n    <string name=\"choose_a_language_for_call_screen\">Elija un idioma para la pantalla de llamadas</string>\n    <string name=\"force_enable_call_recording\">Forzar habilitar la grabación de llamadas</string>\n    <string name=\"force_enable_call_screen\">Forzar habilitar pantalla de llamada (call screen)</string>\n    <string name=\"silence_call_recording_alerts\">Silenciar alertas de grabación de llamadas</string>\n    <!-- Messages -->\n    <string name=\"force_enable_debug_menu\">Forzar habilitar el menú de depuración</string>\n    <string name=\"force_enable_images_via_gphotos\">Forzar habilitar el envío de imágenes mediante ${photos_app_name}</string>\n    <string name=\"force_enable_magic_compose\">Force enable Magic Compose (sugerencias de escritura con AI Bard)</string>\n    <string name=\"force_enable_marking_conversations_as_unread\">Forzar habilitar el marcado de conversaciones como no leídas</string>\n    <string name=\"force_enable_message_organization\">Forzar habilitar la organización de mensajes (supersort)</string>\n    <string name=\"force_enable_nudges_and_birthdays\">Forzar habilitar recordatorios de mensajes (nudges)</string>\n    <string name=\"force_enable_smart_actions_in_notifications\">Forzar habilitar acciones inteligentes (smart reply) en notificaciones</string>\n    <string name=\"force_enable_smart_compose_settings_menu\">Forzar habilitar el menú de configuración de redacción inteligente (smart compose)</string>\n    <string name=\"force_enable_spotlights\">Forzar habilitar menú de configuración de sugerencias (spotlights)</string>\n    <string name=\"force_enable_suggested_stickers_settings_menu\">Forzar habilitar el menú de configuración de pegatinas sugeridas</string>\n    <string name=\"force_enable_verified_sms\">Forzar habilitar el menú de configuración de SMS verificado</string>\n\n    <!-- Revert mods -->\n    <string name=\"revert_mods_for_all_packages\">Revertir mods para todos los packages</string>\n    <string name=\"revert_mods_for_all_packages_confirm\">¿De verdad quieres revertir todas las modificaciones?</string>\n    <string name=\"revert_mods_for_all_packages_explanation\">Presione el botón a continuación para revertir todas las modificaciones aplicadas a todos los packages.</string>\n    <string name=\"revert_mods_for_the_selected_package\">Revertir mods para el package seleccionado</string>\n    <string name=\"revert_mods_for_the_selected_package_confirm\">¿Realmente desea revertir las modificaciones del package %1$s?</string>\n    <string name=\"revert_mods_for_the_selected_package_explanation\">Seleccione un paquete y presione el botón a continuación para revertir las modificaciones aplicadas a ese package específico.</string>\n\n    <!-- Information -->\n    <string name=\"credits\">Creditos</string>\n    <string name=\"how_it_works\">Cómo funciona</string>\n    <string name=\"how_it_works_explanation\">Las modificaciones realizadas por ${app_name} persisten en el tiempo incluso después de la actualización de las aplicaciones de Google, incluso si se desinstalan y reinstalan.\\nEstos cambios se consideran permanentes, pero se pueden revertir fácilmente a través de la sección \"${revert_mods}\" del menú lateral.\\nNo es necesario mantener instalada esta aplicación después de aplicar las modificaciones deseadas.\\n\\nDespués de realizar cambios, puede ser necesario cerrar y abrir forzosamente un par de veces la aplicación que se está intentando modificar antes de que las modificaciones surtan efecto.</string>\n    <string name=\"if_bug_found_open_github_issue\">Si cree que ha encontrado algún error, abra un ticket en Github:</string>\n    <string name=\"made_with_love_by\">Hecho con ❤ por <a href=\"https://github.com/jacopotediosi\">Jacopo Tediosi</a> (y otros increíbles colaboradores)</string>\n    <string name=\"what_is_app_name\">Qué es ${app_name}</string>\n    <string name=\"what_is_it_explanation\">${app_name} es una aplicación que permite, utilizando los privilegios de root, modificar los valores contenidos en la base de datos \"Phenotype\" de Google Play Services, alterando así el comportamiento de las aplicaciones de Google (como ${dialer_app_name} y ${messages_app_name}), desbloqueando funcionalidades experimentales o que solo están disponibles en ciertos países o dispositivos.</string>\n\n    <!-- Warning and error messages -->\n    <string name=\"an_error_has_occurred\">Se ha producido un error</string>\n    <string name=\"app_not_installed_error\">Parece que la aplicación no está instalada.</string>\n    <string name=\"dialer_permission_alert\">${dialer_app_name} no está instalada como una aplicación del sistema y, por lo tanto, no tiene el permiso CAPTURE_AUDIO_OUTPUT.\\nAlgunas funciones, como la grabación de llamadas, pueden no funcionar.</string>\n    <string name=\"new_version_alert\">Hay disponible una nueva versión de ${app_name}.\\n\\nPuedes descargarla desde la página de lanzamientos de Github.</string>\n    <string name=\"phenotype_db_does_not_exist_gms\">Phenotype DB no parece existir.\\n\\nInstala Google Play Services desde Google Play e inténtalo de nuevo.</string>\n    <string name=\"root_access_denied\">Acceso root denegado. Permita el acceso de root.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- GApp names, taken from https://play.google.com/store/apps/details?id=PACKAGE_NAME&hl=fr -->\n    <string name=\"dialer_app_name\">Téléphone de Google</string>\n    <string name=\"messages_app_name\">Messages par Google</string>\n    <string name=\"photos_app_name\" tools:keep=\"@string/photos_app_name\">Google Photos</string>\n\n    <!-- Common -->\n    <string name=\"continue_anyway\">Continuer quand même</string>\n    <string name=\"done\">Fait</string>\n    <string name=\"exit\">Sortir</string>\n    <string name=\"install\">Installer</string>\n    <string name=\"no\">Non</string>\n    <string name=\"or\">Ou</string>\n    <string name=\"yes\">Oui</string>\n    <string name=\"remember\">Rappeler</string>\n    <string name=\"select_package\">Sélectionnez un package</string>\n    <string name=\"unknown\">Inconnu</string>\n\n    <!-- Search and search filters -->\n    <string name=\"additional_filters\">Filtres supplémentaires:</string>\n\n    <string name=\"changed_and_unchanged\">Changé et pas changé</string>\n    <string name=\"changed_only\">Changé seulement</string>\n    <string name=\"changed_status_filter\">Filtrer par changé</string>\n    <string name=\"enabled_status_filter\">Filtrer par état</string>\n    <string name=\"disabled_only\">Désactivé seulement</string>\n    <string name=\"enabled_and_disabled\">Activé et désactivé</string>\n    <string name=\"enabled_only\">Activé seulement</string>\n    <string name=\"expand_additional_filters\">Étendre les filtres supplémentaires</string>\n    <string name=\"search\">Rechercher</string>\n    <string name=\"search_by_flag\">Rechercher par option</string>\n    <string name=\"search_by_package_or_app\">Rechercher par package ou application</string>\n    <string name=\"unchanged_only\">Inchangé seulement</string>\n\n    <!-- Fragment titles -->\n    <string name=\"boolean_mods\">Mods booléens</string>\n    <string name=\"information\">Informations</string>\n    <string name=\"revert_mods\">Annuler les mods</string>\n    <string name=\"suggested_mods\">Mods suggérés</string>\n\n    <!-- Splash screen -->\n    <string name=\"splash_screen_phenotype_gms\">Vérification de la base de données Phenotype…</string>\n    <string name=\"splash_screen_root\">Vérification des permissions racine…</string>\n    <string name=\"splash_screen_root_service\">Connexion au service racine…</string>\n    <string name=\"splash_screen_updates\">Vérification des mises à jour…</string>\n\n    <!-- Suggested mods -->\n    <!-- Dialer -->\n    <string name=\"choose_a_language_for_call_screen\">Choisissez une langue pour le filtrage d\\'appels</string>\n    <string name=\"force_enable_call_recording\">Forcer l\\’activation de l\\’enregistrement des appels</string>\n    <string name=\"force_enable_call_screen\">Forcer l\\'activation du filtrage d\\'appels (call screen)</string>\n    <string name=\"silence_call_recording_alerts\">Mettre sous silence les alertes d\\’enregistrement des appels (${dialer_app_name} &lt;= 94.x uniquement)</string>\n    <!-- Messages -->\n    <string name=\"force_enable_debug_menu\">Forcer l\\'activation du menu de débogage</string>\n    <string name=\"force_enable_images_via_gphotos\">Forcer l\\'activation de l\\'envoi d\\'images via ${photos_app_name}</string>\n    <string name=\"force_enable_magic_compose\">Forcer l\\'activation de Magic Compose (suggestions d\\'écriture avec l\\'IA Bard)</string>\n    <string name=\"force_enable_marking_conversations_as_unread\">Forcer l\\'activation du marquage des conversations comme non lues</string>\n    <string name=\"force_enable_message_organization\">Forcer l\\'activation de l\\'organisation des messages (supersort)</string>\n    <string name=\"force_enable_nudges_and_birthdays\">Forcer l\\'activation des rappels automatiques des messages (nudges)</string>\n    <string name=\"force_enable_smart_actions_in_notifications\">Forcer l\\'activation des actions intelligentes (smart reply) dans les notifications</string>\n    <string name=\"force_enable_smart_compose_settings_menu\">Forcer l\\'activation du menu des paramètres de rédaction intelligente (smart compose)</string>\n    <string name=\"force_enable_spotlights\">Forcer l\\'activation du menu de paramétrage des suggestions d\\'infos pratiques (spotlights)</string>\n    <string name=\"force_enable_suggested_stickers_settings_menu\">Forcer l\\'activation du menu des paramètres d\\'autocollants suggérés</string>\n    <string name=\"force_enable_verified_sms\">Forcer l\\'activation du menu des paramètres SMS vérifiés</string>\n\n    <!-- Revert mods -->\n    <string name=\"revert_mods_for_all_packages\">Annuler les mods pour tous les packages</string>\n    <string name=\"revert_mods_for_all_packages_confirm\">Voulez-vous vraiment annuler tous les mods?</string>\n    <string name=\"revert_mods_for_all_packages_explanation\">Appuyez sur le bouton ci-dessous pour annuler tous les mods appliqués à tous les packages.</string>\n    <string name=\"revert_mods_for_the_selected_package\">Annuler les mods pour le package sélectionné</string>\n    <string name=\"revert_mods_for_the_selected_package_confirm\">Voulez-vous vraiment annuler les mods pour le package %1$s?</string>\n    <string name=\"revert_mods_for_the_selected_package_explanation\">Sélectionnez un package et appuyez sur le bouton ci-dessous pour annuler les mods appliqués à ce package spécifique.</string>\n\n    <!-- Information -->\n    <string name=\"credits\">Crédits</string>\n    <string name=\"how_it_works\">Comment ça fonctionne</string>\n    <string name=\"how_it_works_explanation\">Le modifications appliquées par ${app_name} résistent dans le temps aux mises à jour des applications Google, même après une éventuelle désinstallation et réinstallation de celles-ci.\\nCes modifications doivent donc être considérées comme permanentes, mais elles peuvent facilement être annulées via la section \"${revert_mods}\" du menu latéral.\\nIl n\\'est pas nécessaire de conserver cette application installée après avoir appliqué les modifications souhaitées.\\n\\nAprès avoir effectué des changements, il pourrait être nécessaire de forcer la fermeture et la réouverture de l\\'application que vous essayez de modifier plusieurs fois avant que les modifications ne prennent effet.</string>\n    <string name=\"if_bug_found_open_github_issue\">Si vous pensez avoir trouver un bug, veuillez ouvrir un ticket sur Github:</string>\n    <string name=\"made_with_love_by\">Fait avec ❤ par <a href=\"https://github.com/jacopotediosi\">Jacopo Tediosi</a> (et d\\'autres super contributeurs)</string>\n    <string name=\"what_is_app_name\">Qu\\'est-ce que ${app_name}</string>\n    <string name=\"what_is_it_explanation\">${app_name} est une application qui permet, en utilisant les privilèges de root, de modifier les valeurs contenues dans la base de données \"Phenotype\" de Google Play Services, modifiant ainsi le comportement des applications Google (comme par exemple ${dialer_app_name} et ${messages_app_name}) pour débloquer des fonctionnalités expérimentales ou disponibles uniquement dans certains pays ou appareils.</string>\n\n\n    <!-- Warning and error messages -->\n    <string name=\"an_error_has_occurred\">Une erreur est survenue</string>\n    <string name=\"app_not_installed_error\">Il semble que l\\'application ne soit pas installée.</string>\n    <string name=\"dialer_permission_alert\">${dialer_app_name} n\\'est pas installé en tant qu\\'application système et ne dispose donc pas de l\\'autorisation CAPTURE_AUDIO_OUTPUT.\\nCertaines fonctionnalités, telles que l\\'enregistrement des appels, peuvent ne pas fonctionner.</string>\n    <string name=\"new_version_alert\">Une nouvelle version du ${app_name} est disponible.\\n\\nVous pouvez la télécharger depuis la pages des versions sur Github.</string>\n    <string name=\"phenotype_db_does_not_exist_gms\">La base de données Phenotype n\\’existe pas.\\n\\nInstaller Google Play Services depuis le magasin Google Play et réessayez.</string>\n    <string name=\"root_access_denied\">Accès à la racine refusé. Merci d\\’autoriser l\\’accès à la racine.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-it/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- GApp names, taken from https://play.google.com/store/apps/details?id=PACKAGE_NAME&hl=it -->\n    <string name=\"dialer_app_name\">Telefono Google</string>\n    <string name=\"messages_app_name\">Messaggi</string>\n    <string name=\"photos_app_name\" tools:keep=\"@string/photos_app_name\">Google Foto</string>\n\n    <!-- Common -->\n    <string name=\"continue_anyway\">Continua comunque</string>\n    <string name=\"done\">Fatto</string>\n    <string name=\"exit\">Esci</string>\n    <string name=\"install\">Installa</string>\n    <string name=\"no\">No</string>\n    <string name=\"or\">Oppure</string>\n    <string name=\"yes\">Sì</string>\n    <string name=\"remember\">Ricorda</string>\n    <string name=\"select_package\">Seleziona un package</string>\n    <string name=\"unknown\">Sconosciuto</string>\n\n    <!-- Search and search filters -->\n    <string name=\"additional_filters\">Filtri aggiuntivi:</string>\n    <string name=\"changed_and_unchanged\">Cambiati e non cambiati</string>\n    <string name=\"changed_only\">Solo cambiati</string>\n    <string name=\"changed_status_filter\">Filtra per cambiati</string>\n    <string name=\"disabled_only\">Solo disabilitati</string>\n    <string name=\"enabled_and_disabled\">Abilitati e disabilitati</string>\n    <string name=\"enabled_only\">Solo abilitati</string>\n    <string name=\"enabled_status_filter\">Filtra per stato</string>\n    <string name=\"expand_additional_filters\">Espandi filtri aggiuntivi</string>\n    <string name=\"search\">Cerca</string>\n    <string name=\"search_by_flag\">Cerca per flag</string>\n    <string name=\"search_by_package_or_app\">Cerca per package o applicazione</string>\n    <string name=\"unchanged_only\">Solo non cambiati</string>\n\n    <!-- Fragment titles -->\n    <string name=\"boolean_mods\">Mod booleane</string>\n    <string name=\"information\">Informazioni</string>\n    <string name=\"revert_mods\">Rimuovi mod</string>\n    <string name=\"suggested_mods\">Mod suggerite</string>\n\n    <!-- Splash screen -->\n    <string name=\"splash_screen_phenotype_gms\">Controllando il Phenotype DB…</string>\n    <string name=\"splash_screen_root\">Controllando i permessi di root…</string>\n    <string name=\"splash_screen_root_service\">Connettendo al servizio root…</string>\n    <string name=\"splash_screen_updates\">Controllando gli aggiornamenti…</string>\n\n    <!-- Suggested mods -->\n    <!-- Dialer -->\n    <string name=\"choose_a_language_for_call_screen\">Scegli una lingua per il call screen</string>\n    <string name=\"force_enable_call_recording\">Forza l\\'abilitazione della registrazione chiamate</string>\n    <string name=\"force_enable_call_screen\">Forza l\\'abilitazione del filtro chiamate (call screen)</string>\n    <string name=\"silence_call_recording_alerts\">Silenzia gli audio degli avvisi di registrazione chiamata (solo ${dialer_app_name} &lt;= 94.x)</string>\n    <!-- Messages -->\n    <string name=\"force_enable_debug_menu\">Forza l\\'abilitazione del menu di debug</string>\n    <string name=\"force_enable_images_via_gphotos\">Forza l\\'abilitazione dell\\'invio di immagini via ${photos_app_name}</string>\n    <string name=\"force_enable_magic_compose\">Forza l\\'abilitazione di magic compose (suggerimenti di scrittura con l\\'AI Bard)</string>\n    <string name=\"force_enable_marking_conversations_as_unread\">Forza l\\'abilitazione per segnare conversazioni come non lette</string>\n    <string name=\"force_enable_message_organization\">Forza l\\'abilitazione dell\\'organizzazione dei messaggi (supersort)</string>\n    <string name=\"force_enable_nudges_and_birthdays\">Forza l\\'abilitazione di solleciti e promemoria compleanni (nudges)</string>\n    <string name=\"force_enable_smart_actions_in_notifications\">Forza l\\'abilitazione dei suggerimenti di risposta (smart reply) nelle notifiche</string>\n    <string name=\"force_enable_smart_compose_settings_menu\">Forza l\\'abilitazione del menu impostazioni scrittura intelligente (smart compose)</string>\n    <string name=\"force_enable_spotlights\">Forza l\\'abilitazione del menu impostazioni suggerimenti in evidenza (spotlights)</string>\n    <string name=\"force_enable_suggested_stickers_settings_menu\">Forza l\\'abilitazione del menu impostazioni adesivi consigliati</string>\n    <string name=\"force_enable_verified_sms\">Forza l\\'abilitazione del menu impostazioni SMS verificati</string>\n\n    <!-- Revert mods -->\n    <string name=\"revert_mods_for_all_packages\">Rimuovi le mod per tutti i package</string>\n    <string name=\"revert_mods_for_all_packages_confirm\">Vuoi davvero rimuovere tutte le mod?</string>\n    <string name=\"revert_mods_for_all_packages_explanation\">Premi il bottone sottostante per rimuovere le mod applicate a tutti i package.</string>\n    <string name=\"revert_mods_for_the_selected_package\">Rimuovi le mod per il package selezionato</string>\n    <string name=\"revert_mods_for_the_selected_package_confirm\">Vuoi davvero rimuovere le mod per il package %1$s?</string>\n    <string name=\"revert_mods_for_the_selected_package_explanation\">Seleziona un package e premi il bottone sottostante per rimuovere le mod applicate a quello specifico package.</string>\n\n    <!-- Information -->\n    <string name=\"credits\">Crediti</string>\n    <string name=\"how_it_works\">Come funziona</string>\n    <string name=\"how_it_works_explanation\">Le mod applicate da ${app_name} resistono nel tempo agli aggiornamenti delle applicazioni Google, anche dopo la una loro eventuale disinstallazione e reinstallazione.\\nTali modifiche sono da considerarsi quindi permanenti, ma si possono facilmente annullare tramite la sezione \\\"${revert_mods}\\\" del menù laterale.\\nNon è necessario tenere installata questa app dopo aver applicato le mod desiderate.\\n\\nDopo aver apportato cambiamenti, prima che abbiano effetto, potrebbe essere necessario chiudere forzatamente e riaprire un paio di volte l\\'app che si sta cercando di modificare.</string>\n    <string name=\"if_bug_found_open_github_issue\">Se credi di aver trovato dei bug per favore apri una issue su Github:</string>\n    <string name=\"made_with_love_by\">Creato con ❤ da <a href=\"https://github.com/jacopotediosi\">Jacopo Tediosi</a> (e altri fantastici collaboratori)</string>\n    <string name=\"what_is_app_name\">Che cos\\'è ${app_name}</string>\n    <string name=\"what_is_it_explanation\">${app_name} è una app che permette, sfruttando i privilegi di root, di modificare i valori contenuti nel database \"Phenotype\" di Google Play Services, alterando così il comportamento delle applicazioni Google (come ad esempio ${dialer_app_name} e ${messages_app_name}) per sbloccarne funzionalità sperimentali o disponibili solo per alcuni stati o dispositivi.</string>\n\n    <!-- Warning and error messages -->\n    <string name=\"an_error_has_occurred\">Si è verificato un errore</string>\n    <string name=\"app_not_installed_error\">Sembra che l\\'applicazione non sia installata.</string>\n    <string name=\"dialer_permission_alert\">${dialer_app_name} non è installato come app di sistema e pertanto non dispone del permesso CAPTURE_AUDIO_OUTPUT.\\nAlcune funzioni, come la registrazione chiamate, potrebbero non funzionare.</string>\n    <string name=\"new_version_alert\">E\\' disponibile una nuova versione di ${app_name}.\\n\\nPuoi scaricarla dalla pagina Releases di Github.</string>\n    <string name=\"phenotype_db_does_not_exist_gms\">Sembra che non esista il Phenotype DB.\\n\\nInstalla Google Play Services da Google Play e riprova.</string>\n    <string name=\"root_access_denied\">Impossibile ottenere i permessi di root. Per favore consenti l\\'accesso root.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Application dark theme -->\n    <style name=\"AppTheme\" parent=\"BaseTheme\">\n        <!-- Set status bar and navigation bar color to transparent -->\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n    </style>\n\n    <!-- Application dark base theme -->\n    <style name=\"BaseTheme\" parent=\"Theme.Material3.Dark.NoActionBar\">\n        <item name=\"colorPrimary\">@color/md_theme_dark_primary</item>\n        <item name=\"colorOnPrimary\">@color/md_theme_dark_onPrimary</item>\n        <item name=\"colorPrimaryContainer\">@color/md_theme_dark_primaryContainer</item>\n        <item name=\"colorOnPrimaryContainer\">@color/md_theme_dark_onPrimaryContainer</item>\n        <item name=\"colorSecondary\">@color/md_theme_dark_secondary</item>\n        <item name=\"colorOnSecondary\">@color/md_theme_dark_onSecondary</item>\n        <item name=\"colorSecondaryContainer\">@color/md_theme_dark_secondaryContainer</item>\n        <item name=\"colorOnSecondaryContainer\">@color/md_theme_dark_onSecondaryContainer</item>\n        <item name=\"colorTertiary\">@color/md_theme_dark_tertiary</item>\n        <item name=\"colorOnTertiary\">@color/md_theme_dark_onTertiary</item>\n        <item name=\"colorTertiaryContainer\">@color/md_theme_dark_tertiaryContainer</item>\n        <item name=\"colorOnTertiaryContainer\">@color/md_theme_dark_onTertiaryContainer</item>\n        <item name=\"colorError\">@color/md_theme_dark_error</item>\n        <item name=\"colorErrorContainer\">@color/md_theme_dark_errorContainer</item>\n        <item name=\"colorOnError\">@color/md_theme_dark_onError</item>\n        <item name=\"colorOnErrorContainer\">@color/md_theme_dark_onErrorContainer</item>\n        <item name=\"android:colorBackground\">@color/md_theme_dark_background</item>\n        <item name=\"colorOnBackground\">@color/md_theme_dark_onBackground</item>\n        <item name=\"colorSurface\">@color/md_theme_dark_surface</item>\n        <item name=\"colorOnSurface\">@color/md_theme_dark_onSurface</item>\n        <item name=\"colorSurfaceVariant\">@color/md_theme_dark_surfaceVariant</item>\n        <item name=\"colorOnSurfaceVariant\">@color/md_theme_dark_onSurfaceVariant</item>\n        <item name=\"colorOutline\">@color/md_theme_dark_outline</item>\n        <item name=\"colorOnSurfaceInverse\">@color/md_theme_dark_inverseOnSurface</item>\n        <item name=\"colorSurfaceInverse\">@color/md_theme_dark_inverseSurface</item>\n        <item name=\"colorPrimaryInverse\">@color/md_theme_dark_inversePrimary</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-notnight-v23/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Override application light theme on API 23+ (because on lower versions light status bar is\n    not supported) to set status bar color to transparent -->\n    <style name=\"AppTheme\" parent=\"BaseTheme\">\n        <!-- Set status bar transparent and light -->\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-notnight-v27/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Override application light theme on API 27+ (because on lower versions light navigation bar is\n    not supported) to set status bar and navigation bar color to transparent -->\n    <style name=\"AppTheme\" parent=\"BaseTheme\">\n        <!-- Set status bar transparent and light -->\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n\n        <!-- Set navigation bar transparent and light -->\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:windowLightNavigationBar\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.0.2'\n        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.3'\n        classpath 'com.likethesalad.android:stem-plugin:2.4.1'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 26 10:10:02 CEST 2023\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.1.1-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true"
  },
  {
    "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",
    "content": "dependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        maven { url 'https://jitpack.io' }\n    }\n}\nrootProject.name = \"GAppsMod\"\ninclude ':app'\n"
  }
]