[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n### Android template\n# Built application files\n*.apk\n*.aar\n*.ap_\n*.aab\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n#  Uncomment the following line in case you need and you don't have the release build type files in your app\n# release/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n# IntelliJ\n*.iml\n.idea/workspace.xml\n.idea/tasks.xml\n.idea/gradle.xml\n.idea/assetWizardSettings.xml\n.idea/dictionaries\n.idea/libraries\n# Android Studio 3 in .gitignore file.\n.idea/caches\n.idea/modules.xml\n# Comment next line if keeping position of elements in Navigation Editor is relevant for you\n.idea/navEditor.xml\n\n# Keystore files\n# Uncomment the following lines if you do not want to check your keystore files in.\n#*.jks\n#*.keystore\n\n# External native build folder generated in Android Studio 2.2 and later\n.externalNativeBuild\n.cxx/\n\n# Google Services (e.g. APIs or Firebase)\n# google-services.json\n\n# Freeline\nfreeline.py\nfreeline/\nfreeline_project_description.json\n\n# fastlane\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots\nfastlane/test_output\nfastlane/readme.md\n\n# Version control\nvcs.xml\n\n# lint\nlint/intermediates/\nlint/generated/\nlint/outputs/\nlint/tmp/\n# lint/reports/\n\n# Android Profiling\n*.hprof\n\n"
  },
  {
    "path": ".idea/.gitignore",
    "content": "# Default ignored files\n/shelf/\n/workspace.xml\n"
  },
  {
    "path": ".idea/.name",
    "content": "Pixelify Google Photos"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <bytecodeTargetLevel target=\"11\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"DesignSurface\">\n    <option name=\"filePathToZoomLevelMap\">\n      <map>\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/drawable/ic_export.xml\" value=\"0.21481481481481482\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/drawable/ic_import.xml\" value=\"0.2203125\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/drawable/ic_launcher_background.xml\" value=\"0.1125\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/drawable/ic_share.xml\" value=\"0.21481481481481482\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/layout/activity_main.xml\" value=\"0.25\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/layout/advanced_options_activity.xml\" value=\"0.1537037037037037\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/layout/feature_customize.xml\" value=\"0.20416666666666666\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/menu/menu_activity_main.xml\" value=\"0.20416666666666666\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml\" value=\"0.20833333333333334\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml\" value=\"0.20833333333333334\" />\n        <entry key=\"..\\:/AndroidStudioProjects/Pixelify-Google-Photos/app/src/main/res/xml/provider_paths.xml\" value=\"0.20416666666666666\" />\n        <entry key=\"..\\:/Users/SayantanRC/AndroidStudioProjects/PixelifyGooglePhotos/app/src/main/res/layout/activity_main.xml\" value=\"0.20416666666666666\" />\n      </map>\n    </option>\n  </component>\n  <component name=\"ExternalStorageConfigurationManager\" enabled=\"true\" />\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_11\" default=\"true\" project-jdk-name=\"11\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 BaltiApps\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Pixelify-Google-Photos\nLSPosed / EdXposed module to add Google Pixel features on Google Photos for any device.  \nTested on EdXposed by [Jim Wu](https://github.com/MlgmXyysd)  \n\n[LSPosed module repo](https://github.com/Xposed-Modules-Repo/balti.xposed.pixelifygooglephotos.git)  \n[Development repo](https://github.com/BaltiApps/Pixelify-Google-Photos.git)  \n\n[Telegram group link](https://t.me/pixelifyGooglePhotos)  \n\n### Steps to use:\n1. Install Magisk, [LSPosed](https://github.com/LSPosed/LSPosed) Or [EdXposed](https://github.com/ElderDrivers/EdXposed).  \n2. Install the apk of this app (available from [Releases](https://github.com/BaltiApps/Pixelify-Google-Photos/releases) page.)  \n3. Open LSPosed / EdXposed app and enable the module. For LSPosed, Google Photos will be automatically selected.  \n4. Reboot. Enjoy. (If needed, you might need to clear data of Google Photos app).  \n\n### How does this module work?\nIt simply hooks on to `hasSystemFeature()` method under `android.app.ApplicationPackageManager` class. \nThen when Google Photos checks the relevant features which are expected only on Pixel devices, the module passes `true`. \nThus Google Photos enables Pixel-Exclusive features.  \nThe features being \"spoofed\" can be found from:  \n[Dot OS sources](https://github.com/DotOS/android_vendor_dot/blob/55f1c26bb6dbb1175d96cf538ae113618caf7d06/prebuilt/common/etc/pixel_2016_exclusive.xml)  \n[PixelFeatureDrops magisk module](https://github.com/ayush5harma/PixelFeatureDrops/tree/master/system/etc/sysconfig)  \nThis module can also spoof some of the `build.prop` information like `BRAND`, `MANUFACTURER`, `MODEL`, `FINGERPRINT` of some Pixel devices.  \n\n### Disclaimer!!\nThe user takes sole responsibility for any damage that might arise due to use of this module.  \nThis includes physical damage (to device), injury, data loss, and also legal matters.  \nThis project was made as a learning initiative and the developer cannot be held liable in any way for the use of it.\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build"
  },
  {
    "path": "app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n}\n\nandroid {\n    compileSdk 31\n\n    defaultConfig {\n        applicationId \"balti.xposed.pixelifygooglephotos\"\n        minSdk 21\n        targetSdk 31\n        versionCode 5\n        versionName \"4.1\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\ndependencies {\n\n    implementation 'androidx.core:core-ktx:1.7.0'\n    implementation 'androidx.appcompat:appcompat:1.4.1'\n    implementation 'com.google.android.material:material:1.5.0'\n\n    // Xposed API\n    compileOnly 'de.robv.android.xposed:api:82'\n    compileOnly 'de.robv.android.xposed:api:82:sources'\n}"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "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    package=\"balti.xposed.pixelifygooglephotos\">\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <queries>\n        <package android:name=\"com.google.android.apps.photos\" />\n    </queries>\n\n    <application\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:theme=\"@style/Theme.PixelifyGooglePhotos\" >\n\n        <meta-data\n            android:name=\"xposedmodule\"\n            android:value=\"true\" />\n        <meta-data\n            android:name=\"xposeddescription\"\n            android:value=\"Add Google Pixel features in Google Photos on any device.\" />\n        <meta-data\n            android:name=\"xposedsharedprefs\"\n            android:value=\"true\" />\n        <meta-data\n            android:name=\"xposedminversion\"\n            android:value=\"93\" />\n        <meta-data\n            android:name=\"xposedscope\"\n            android:resource=\"@array/module_scope\" />\n\n        <activity android:name=\".ActivityMain\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity android:name=\".FeatureCustomize\"/>\n\n        <activity android:name=\".AdvancedOptionsActivity\"/>\n\n        <!--Guide at: https://infinum.com/the-capsized-eight/share-files-using-fileprovider-->\n        <provider\n            android:authorities=\"${applicationId}\"\n            android:name=\"androidx.core.content.FileProvider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/provider_paths\"/>\n        </provider>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/assets/xposed_init",
    "content": "balti.xposed.pixelifygooglephotos.FeatureSpoofer\nbalti.xposed.pixelifygooglephotos.DeviceSpoofer"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/ActivityMain.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.graphics.Paint\nimport android.net.Uri\nimport android.os.AsyncTask\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.Menu\nimport android.view.MenuItem\nimport android.view.View\nimport android.widget.*\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.widget.SwitchCompat\nimport androidx.core.content.FileProvider\nimport balti.xposed.pixelifygooglephotos.Constants.CONF_EXPORT_NAME\nimport balti.xposed.pixelifygooglephotos.Constants.FIELD_LATEST_VERSION_CODE\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_DEVICE_TO_SPOOF\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_ENABLE_VERBOSE_LOGS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_LAST_VERSION\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_OVERRIDE_ROM_FEATURE_LEVELS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_MANUAL\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_FEATURES_LIST\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_STRICTLY_CHECK_GOOGLE_PHOTOS\nimport balti.xposed.pixelifygooglephotos.Constants.RELEASES_URL\nimport balti.xposed.pixelifygooglephotos.Constants.RELEASES_URL2\nimport balti.xposed.pixelifygooglephotos.Constants.SHARED_PREF_FILE_NAME\nimport balti.xposed.pixelifygooglephotos.Constants.TELEGRAM_GROUP\nimport balti.xposed.pixelifygooglephotos.Constants.UPDATE_INFO_URL\nimport balti.xposed.pixelifygooglephotos.Constants.UPDATE_INFO_URL2\nimport com.google.android.material.snackbar.Snackbar\nimport org.json.JSONObject\nimport java.io.ByteArrayOutputStream\nimport java.io.File\nimport java.net.URL\n\n\nclass ActivityMain: AppCompatActivity(R.layout.activity_main) {\n\n    /**\n     * Normally [MODE_WORLD_READABLE] causes a crash.\n     * But if \"xposedsharedprefs\" flag is present in AndroidManifest,\n     * then the file is accordingly taken care by lsposed framework.\n     *\n     * If an exception is thrown, means module is not enabled,\n     * hence Android throws a security exception.\n     */\n    private val pref by lazy {\n        try {\n            getSharedPreferences(SHARED_PREF_FILE_NAME, MODE_WORLD_READABLE)\n        } catch (_: Exception){\n            null\n        }\n    }\n\n    private fun showRebootSnack(){\n        if (pref == null) return // don't display snackbar if module not active.\n        val rootView = findViewById<ScrollView>(R.id.root_view_for_snackbar)\n        Snackbar.make(rootView, R.string.please_force_stop_google_photos, Snackbar.LENGTH_SHORT).show()\n    }\n\n    /**\n     * Animate the \"Feature flags changed\" textview and hide it after showing for sometime.\n     */\n    private fun peekFeatureFlagsChanged(textView: TextView){\n        textView.run {\n            alpha = 1.0f\n            animate().alpha(0.0f).apply {\n                duration = 1000\n                startDelay = 3000\n            }.start()\n        }\n    }\n\n    private val utils by lazy { Utils() }\n\n    /**\n     * Activity launcher for [FeatureCustomize] activity.\n     * If user presses \"Save\" on [FeatureCustomize] activity, then result code is RESULT_OK.\n     * Then show prompt to force stop Google Photos.\n     */\n    private val childActivityLauncher =\n        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {\n            if (it.resultCode == Activity.RESULT_OK) {\n                showRebootSnack()\n            }\n        }\n\n    /**\n     * Close and reopen the activity.\n     * For some reason, invalidate or recreate() does not refresh the switches.\n     */\n    private fun restartActivity(){\n        finish()\n        startActivity(intent)\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        /**\n         * Check if [pref] is not null. If it is, then module is not enabled.\n         */\n        if (pref == null){\n            AlertDialog.Builder(this)\n                .setMessage(R.string.module_not_enabled)\n                .setPositiveButton(R.string.close) {_, _ ->\n                    finish()\n                }\n                .setCancelable(false)\n                .show()\n        }\n\n        /**\n         * Link to xml views.\n         */\n        val resetSettings = findViewById<Button>(R.id.reset_settings)\n        val customizeFeatureFlags = findViewById<LinearLayout>(R.id.customize_feature_flags)\n        val featureFlagsChanged = findViewById<TextView>(R.id.feature_flags_changed)\n        val overrideROMFeatureLevels = findViewById<SwitchCompat>(R.id.override_rom_feature_levels)\n        val switchEnforceGooglePhotos = findViewById<SwitchCompat>(R.id.spoof_only_in_google_photos_switch)\n        val deviceSpooferSpinner = findViewById<Spinner>(R.id.device_spoofer_spinner)\n        val forceStopGooglePhotos = findViewById<Button>(R.id.force_stop_google_photos)\n        val openGooglePhotos = findViewById<ImageButton>(R.id.open_google_photos)\n        val advancedOptions = findViewById<TextView>(R.id.advanced_options)\n        val telegramLink = findViewById<TextView>(R.id.telegram_group)\n        val updateAvailableLink = findViewById<TextView>(R.id.update_available_link)\n        val confExport = findViewById<ImageButton>(R.id.conf_export)\n        val confImport = findViewById<ImageButton>(R.id.conf_import)\n\n        /**\n         * Set default spoof device to [DeviceProps.defaultDeviceName].\n         * Set check for google photos as `false`.\n         * Set default feature levels to spoof.\n         * Restart the activity.\n         */\n        resetSettings.setOnClickListener {\n            pref?.edit()?.run {\n                putString(PREF_DEVICE_TO_SPOOF, DeviceProps.defaultDeviceName)\n                putBoolean(PREF_OVERRIDE_ROM_FEATURE_LEVELS, true)\n                putBoolean(PREF_STRICTLY_CHECK_GOOGLE_PHOTOS, true)\n                putStringSet(\n                    PREF_SPOOF_FEATURES_LIST,\n                    DeviceProps.defaultFeatures.map { it.displayName }.toSet()\n                )\n                putBoolean(PREF_ENABLE_VERBOSE_LOGS, false)\n                putBoolean(PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE, false)\n                putString(PREF_SPOOF_ANDROID_VERSION_MANUAL, null)\n                apply()\n            }\n            restartActivity()\n        }\n\n        /**\n         * See [FeatureSpoofer.featuresNotToSpoof].\n         */\n        overrideROMFeatureLevels.apply {\n            isChecked = pref?.getBoolean(PREF_OVERRIDE_ROM_FEATURE_LEVELS, true) ?: false\n            setOnCheckedChangeListener { _, isChecked ->\n                pref?.edit()?.run {\n                    putBoolean(PREF_OVERRIDE_ROM_FEATURE_LEVELS, isChecked)\n                    apply()\n                    showRebootSnack()\n                }\n            }\n        }\n\n        /**\n         * See [FeatureSpoofer].\n         */\n        switchEnforceGooglePhotos.apply {\n            isChecked = pref?.getBoolean(PREF_STRICTLY_CHECK_GOOGLE_PHOTOS, true) ?: false\n            setOnCheckedChangeListener { _, isChecked ->\n                pref?.edit()?.run {\n                    putBoolean(PREF_STRICTLY_CHECK_GOOGLE_PHOTOS, isChecked)\n                    apply()\n                    showRebootSnack()\n                }\n            }\n        }\n\n        /**\n         * See [DeviceSpoofer].\n         */\n        deviceSpooferSpinner.apply {\n            val deviceNames = DeviceProps.allDevices.map { it.deviceName }\n            val aa = ArrayAdapter(this@ActivityMain,android.R.layout.simple_spinner_item, deviceNames)\n\n            aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)\n            adapter = aa\n            val defaultSelection = pref?.getString(PREF_DEVICE_TO_SPOOF, DeviceProps.defaultDeviceName)\n            /** Second argument is `false` to prevent calling [peekFeatureFlagsChanged] on initialization */\n            setSelection(aa.getPosition(defaultSelection), false)\n\n            onItemSelectedListener = object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {\n                    val deviceName = aa.getItem(position)\n                    pref?.edit()?.apply {\n                        putString(PREF_DEVICE_TO_SPOOF, deviceName)\n                        putStringSet(\n                            PREF_SPOOF_FEATURES_LIST,\n                            DeviceProps.getFeaturesUpToFromDeviceName(deviceName)\n                        )\n                        apply()\n                    }\n\n                    peekFeatureFlagsChanged(featureFlagsChanged)\n                    showRebootSnack()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {}\n            }\n        }\n\n        advancedOptions.apply {\n            paintFlags = Paint.UNDERLINE_TEXT_FLAG\n            setOnClickListener {\n                childActivityLauncher.launch(Intent(this@ActivityMain, AdvancedOptionsActivity::class.java))\n            }\n        }\n\n        /**\n         * See [Utils.forceStopPackage].\n         */\n        forceStopGooglePhotos.setOnClickListener {\n            utils.forceStopPackage(Constants.PACKAGE_NAME_GOOGLE_PHOTOS, this)\n        }\n\n        /**\n         * See [Utils.openApplication].\n         */\n        openGooglePhotos.setOnClickListener {\n            utils.openApplication(Constants.PACKAGE_NAME_GOOGLE_PHOTOS, this)\n        }\n\n        /**\n         * Launch [FeatureCustomize] to fine select the features.\n         */\n        customizeFeatureFlags.setOnClickListener {\n            childActivityLauncher.launch(Intent(this, FeatureCustomize::class.java))\n        }\n\n        /**\n         * Open telegram group.\n         */\n        telegramLink.apply {\n            paintFlags = Paint.UNDERLINE_TEXT_FLAG\n            setOnClickListener {\n                openWebLink(TELEGRAM_GROUP)\n            }\n        }\n\n        /**\n         * Open config share options.\n         * Also see [Utils.writeConfigFile].\n         */\n        confExport.setOnClickListener {\n            AlertDialog.Builder(this).apply {\n                setTitle(R.string.export_config)\n                setMessage(R.string.export_config_desc)\n                setPositiveButton(R.string.share){_, _ ->\n                    shareConfFile()\n                }\n                setNegativeButton(R.string.save){_, _ ->\n                    saveConfFile()\n                }\n                setNeutralButton(android.R.string.cancel, null)\n            }\n                .show()\n        }\n\n        confImport.setOnClickListener {\n            AlertDialog.Builder(this).apply {\n                setTitle(R.string.import_config)\n                setMessage(R.string.import_config_desc)\n                setPositiveButton(android.R.string.ok){_, _ ->\n                    importConfFile()\n                }\n                setNegativeButton(android.R.string.cancel, null)\n            }\n                .show()\n        }\n\n        /**\n         * Check if changelogs need to be shown when upgrading from older version.\n         */\n        pref?.apply {\n            val thisVersion = BuildConfig.VERSION_CODE\n            if (getInt(PREF_LAST_VERSION, 0) < thisVersion){\n                showChangeLog()\n                edit().apply {\n                    putInt(PREF_LAST_VERSION, thisVersion)\n                    apply()\n                }\n            }\n        }\n\n        /**\n         * Check for updates in background thread.\n         * Yes AsyncTask is deprecated, but it works fine and for such a short network operation\n         * it is useless to try coroutine or something like that.\n         */\n        AsyncTask.execute {\n            isUpdateAvailable()?.let { url ->\n                runOnUiThread {\n                    updateAvailableLink.apply {\n                        paintFlags = Paint.UNDERLINE_TEXT_FLAG\n                        visibility = View.VISIBLE\n                        setOnClickListener {\n                            openWebLink(url)\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Method to show latest changes.\n     */\n    private fun showChangeLog(){\n        AlertDialog.Builder(this)\n            .setTitle(R.string.version_head)\n            .setMessage(R.string.version_desc)\n            .setPositiveButton(android.R.string.ok, null)\n            .show()\n    }\n\n    /**\n     * Populate menu.\n     * Menu contains option to show changelog.\n     */\n    override fun onCreateOptionsMenu(menu: Menu?): Boolean {\n        menuInflater.inflate(R.menu.menu_activity_main, menu)\n        return super.onCreateOptionsMenu(menu)\n    }\n\n    /**\n     * Click listener on menu.\n     */\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        when(item.itemId){\n            R.id.menu_changelog -> showChangeLog()\n        }\n        return super.onOptionsItemSelected(item)\n    }\n\n    /**\n     * Check if update is available. Return url string of Github Releases page if update is present.\n     * Else returns null.\n     *\n     * This checks for update in two repositories.\n     * The original BaltiApps repository, as well as LSPosed repository.\n     * If update is available in any of them it send the respective repo's Release page link.\n     */\n    private fun isUpdateAvailable(): String? {\n\n        fun getUpdateStatus(url: String): Boolean {\n            var jsonString = \"\"\n            val baos = ByteArrayOutputStream()\n\n            /**\n             * Get contents of the file into a string.\n             */\n            try {\n                URL(url).openStream().use { input ->\n                    baos.use { output ->\n                        input.copyTo(output)\n                    }\n                    jsonString = baos.toString()\n                }\n            } catch (_: Exception) {\n                return false\n            }\n\n            /**\n             * Parse the string as a JSON object.\n             */\n            return if (jsonString.isNotBlank()) {\n                try {\n                    val json = JSONObject(jsonString)\n                    val remoteVersion = json.getInt(FIELD_LATEST_VERSION_CODE)\n                    BuildConfig.VERSION_CODE < remoteVersion\n                } catch (_: Exception) {\n                    false\n                }\n            } else false\n        }\n\n        /**\n         * Check both repositories.\n         */\n        return when {\n            getUpdateStatus(UPDATE_INFO_URL) -> RELEASES_URL\n            getUpdateStatus(UPDATE_INFO_URL2) -> RELEASES_URL2\n            else -> null\n        }\n    }\n\n    /**\n     * Open any url link\n     */\n    fun openWebLink(url: String){\n        startActivity(Intent(Intent.ACTION_VIEW).apply {\n            data = Uri.parse(url)\n        })\n    }\n\n    /**\n     * Creates configuration export file to internal cache.\n     * Shares it to other apps.\n     */\n    private fun shareConfFile(){\n\n        try {\n            val confFile = File(cacheDir, CONF_EXPORT_NAME)\n            val uriFromFile = Uri.fromFile(confFile)\n\n            confFile.delete()\n            utils.writeConfigFile(this, uriFromFile, pref)\n\n            val confFileShareUri =\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)\n                    FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, confFile)\n                else uriFromFile\n\n            Intent().run {\n\n                action = Intent.ACTION_SEND\n                type = \"*/*\"\n                flags = Intent.FLAG_GRANT_READ_URI_PERMISSION\n\n                this.putExtra(Intent.EXTRA_STREAM, confFileShareUri)\n                startActivity(Intent.createChooser(this, getString(R.string.share_config_file)))\n            }\n        }\n        catch (e: Exception){\n            e.printStackTrace()\n            Toast.makeText(this, \"${getString(R.string.share_error)}: ${e.message}\", Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    /**\n     * Open a storage location on the device to export the configuration as a document.\n     * Uses intent with action [Intent.ACTION_CREATE_DOCUMENT]\n     * Also see [configCreateLauncher].\n     *\n     * Derived from https://gist.github.com/neonankiti/05922cf0a44108a2e2732671ed9ef386\n     */\n    private fun saveConfFile(){\n        val openIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {\n            // filter to only show openable items.\n            addCategory(Intent.CATEGORY_OPENABLE)\n\n            // Create a file with the requested Mime type\n            type = \"*/*\"\n            putExtra(Intent.EXTRA_TITLE, CONF_EXPORT_NAME)\n        }\n        Toast.makeText(this, R.string.select_a_location, Toast.LENGTH_SHORT).show()\n        configCreateLauncher.launch(openIntent)\n    }\n\n    /**\n     * Intent launcher to start system file picker UI to select location of export.\n     * The Uri of the location is present in result.\n     * Then call [Utils.writeConfigFile] using that Uri.\n     */\n    private val configCreateLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){\n        try {\n            if (it.resultCode == Activity.RESULT_OK) {\n                utils.writeConfigFile(this, it.data!!.data!!, pref)\n                Toast.makeText(this, R.string.export_complete, Toast.LENGTH_SHORT).show()\n            }\n        }\n        catch (e: Exception){\n            e.printStackTrace()\n            Toast.makeText(this, \"${getString(R.string.share_error)}: ${e.message}\", Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    /**\n     * Read a JSON file to get the configurations.\n     * Opens system file picker to select the file.\n     *\n     * https://developer.android.com/training/data-storage/shared/documents-files#open-file\n     */\n    private fun importConfFile(){\n        val openIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {\n            addCategory(Intent.CATEGORY_OPENABLE)\n            type = \"*/*\"\n        }\n        configOpenLauncher.launch(openIntent)\n    }\n\n    private val configOpenLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){\n        try {\n            if (it.resultCode == Activity.RESULT_OK) {\n                utils.readConfigFile(this, it.data!!.data!!, pref)\n                Toast.makeText(this, R.string.import_complete, Toast.LENGTH_SHORT).show()\n                restartActivity()\n            }\n        }\n        catch (e: Exception){\n            e.printStackTrace()\n            Toast.makeText(this, \"${getString(R.string.read_error)}: ${e.message}\", Toast.LENGTH_SHORT).show()\n        }\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/AdvancedOptionsActivity.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.widget.*\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.isVisible\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_DEVICE_TO_SPOOF\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_ENABLE_VERBOSE_LOGS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_MANUAL\nimport balti.xposed.pixelifygooglephotos.Constants.SHARED_PREF_FILE_NAME\n\nclass AdvancedOptionsActivity: AppCompatActivity(R.layout.advanced_options_activity) {\n\n    private val pref by lazy {\n        getSharedPreferences(SHARED_PREF_FILE_NAME, MODE_WORLD_READABLE)\n    }\n\n    private val verboseLogging by lazy { findViewById<CheckBox>(R.id.verbose_logging) }\n    private val deviceNameLabel by lazy { findViewById<TextView>(R.id.device_name_label) }\n    private val androidVersionRadioGroup by lazy { findViewById<RadioGroup>(R.id.android_version_radio_group) }\n    private val deviceAndroidVersion by lazy { findViewById<TextView>(R.id.device_android_version) }\n    private val androidVersionSpinner by lazy { findViewById<Spinner>(R.id.android_version_spinner) }\n    private val save by lazy { findViewById<Button>(R.id.save_advanced_option) }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        if (pref == null) return\n\n        verboseLogging.isChecked = pref.getBoolean(PREF_ENABLE_VERBOSE_LOGS, false)\n\n        /**\n         * Get the current spoofing device an its android version.\n         */\n        val deviceNameInPreference = pref.getString(PREF_DEVICE_TO_SPOOF, DeviceProps.defaultDeviceName)\n        val spoofDevice = DeviceProps.getDeviceProps(deviceNameInPreference)\n        deviceNameLabel.text = spoofDevice?.deviceName\n        deviceAndroidVersion.text = spoofDevice?.androidVersion?.label\n\n        /**\n         * String list of all [DeviceProps.AndroidVersion.label].\n         */\n        val allVersionLabels = DeviceProps.allAndroidVersions.map { it.label }\n\n        /**\n         * Set spinner for manually setting android version.\n         */\n        androidVersionSpinner.apply {\n            val aa = ArrayAdapter(\n                this@AdvancedOptionsActivity,\n                android.R.layout.simple_spinner_item,\n                allVersionLabels\n            )\n\n            aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)\n            adapter = aa\n            val defaultSelection = pref?.getString(PREF_SPOOF_ANDROID_VERSION_MANUAL, null)\n            setSelection(aa.getPosition(defaultSelection))\n        }\n\n        /**\n         * Set default radio button.\n         */\n        androidVersionRadioGroup.apply {\n\n            // hide spinner if not to be manually set.\n            setOnCheckedChangeListener { _, checkedId ->\n                androidVersionSpinner.isVisible = checkedId == R.id.manually_set_android_version\n            }\n\n            val manualVersion = pref.getString(PREF_SPOOF_ANDROID_VERSION_MANUAL, null)?.trim()\n\n            check(\n                when {\n                    pref.getBoolean(PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE, false) -> R.id.follow_spoof_device_version\n                    manualVersion != null && manualVersion in allVersionLabels -> R.id.manually_set_android_version\n                    else -> R.id.dont_spoof_android_version\n                }\n            )\n        }\n\n        save.setOnClickListener {\n            savePreferences()\n        }\n\n    }\n\n    /**\n     * Function to save all options.\n     */\n    private fun savePreferences(){\n        pref?.edit()?.run {\n\n            /** Option for verbose log. */\n            putBoolean(PREF_ENABLE_VERBOSE_LOGS, verboseLogging.isChecked)\n\n            when(androidVersionRadioGroup.checkedRadioButtonId){\n\n                /** If not to spoof, disable both preferences */\n                R.id.dont_spoof_android_version -> {\n                    putBoolean(PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE, false)\n                    putString(PREF_SPOOF_ANDROID_VERSION_MANUAL, null)\n                }\n\n                /** Enable only [PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE]\n                 * if to follow spoof device android version.\n                 */\n                R.id.follow_spoof_device_version -> {\n                    putBoolean(PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE, true)\n                    putString(PREF_SPOOF_ANDROID_VERSION_MANUAL, null)\n                }\n\n                /**\n                 * If manually set, then get the label from spinner itself\n                 * and set the value for preference [PREF_SPOOF_ANDROID_VERSION_MANUAL].\n                 */\n                R.id.manually_set_android_version -> {\n                    putBoolean(PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE, false)\n                    putString(\n                        PREF_SPOOF_ANDROID_VERSION_MANUAL,\n                        androidVersionSpinner.selectedItem.toString()\n                    )\n                }\n\n            }\n\n            apply()\n        }\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/Constants.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nobject Constants {\n\n    val PACKAGE_NAME_GOOGLE_PHOTOS = \"com.google.android.apps.photos\"\n\n    val TELEGRAM_GROUP = \"https://t.me/pixelifyGooglePhotos\"\n\n    val UPDATE_INFO_URL = \"https://raw.githubusercontent.com/BaltiApps/Pixelify-Google-Photos/main/update_info.json\"\n    val UPDATE_INFO_URL2 = \"https://raw.githubusercontent.com/Xposed-Modules-Repo/balti.xposed.pixelifygooglephotos/main/update_info.json\"\n    val RELEASES_URL = \"https://github.com/BaltiApps/Pixelify-Google-Photos/releases\"\n    val RELEASES_URL2 = \"https://github.com/Xposed-Modules-Repo/balti.xposed.pixelifygooglephotos/releases\"\n\n    val FIELD_LATEST_VERSION_CODE = \"latest_version_code\"\n\n    val SHARED_PREF_FILE_NAME = \"prefs\"\n\n    val CONF_EXPORT_NAME = \"pgp_conf.json\"\n\n    val PREF_SPOOF_FEATURES_LIST = \"PREF_SPOOF_FEATURES_LIST\"\n    val PREF_DEVICE_TO_SPOOF = \"PREF_DEVICE_TO_SPOOF\"\n    val PREF_STRICTLY_CHECK_GOOGLE_PHOTOS = \"PREF_STRICTLY_CHECK_GOOGLE_PHOTOS\"\n    val PREF_OVERRIDE_ROM_FEATURE_LEVELS = \"PREF_OVERRIDE_ROM_FEATURE_LEVELS\"\n    val PREF_ENABLE_VERBOSE_LOGS = \"PREF_ENABLE_VERBOSE_LOGS\"\n    val PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE = \"PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE\"\n    val PREF_SPOOF_ANDROID_VERSION_MANUAL = \"PREF_SPOOF_ANDROID_VERSION_MANUAL\"\n    val PREF_LAST_VERSION = \"PREF_LAST_VERSION\"\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/DeviceProps.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\n/**\n * Build values taken from:\n * Pixel 6:\n * https://github.com/DotOS/android_frameworks_base/blob/dot12/core/java/com/android/internal/util/custom/PixelPropsUtils.java\n * Pixel 2:\n * https://gist.github.com/markstachowski/4069f15c5c989827b9e64c0aec045434\n * Pixel 5a:\n * https://github.com/LineageOS/android_device_google_barbet/blob/lineage-18.1/lineage_barbet.mk\n * All other pixels:\n * https://github.com/orgs/Pixel-Props/repositories\n * Also from\n * https://github.com/DotOS/android_frameworks_base/commit/3f7ea7d070017ed1f38035333f084865865698b2\n *\n * Features taken from:\n * https://t.me/PixelProps\n * https://github.com/DotOS/android_vendor_dot/blob/dot12/prebuilt/common/etc/pixel_2016_exclusive.xml\n * https://github.com/ayush5harma/PixelFeatureDrops/tree/master/system/etc/sysconfig\n */\nobject DeviceProps {\n\n    /**\n     * Class to store different feature flags for different pixels.\n     * @param displayName String to show to user to customize flag selection. Example \"Pixel 2020\"\n     * Also note that these display names are what is actually stored in shared preferences.\n     * The actual feature flags are then derived from the display names.\n     * @param featureFlags List of actual features spoofed to Google Photos for that particular [displayName].\n     * Example, for [displayName] = \"Pixel 2020\", [featureFlags] = listOf(\"com.google.android.feature.PIXEL_2020_EXPERIENCE\")\n     */\n    class Features(\n        val displayName: String,\n        val featureFlags: List<String>,\n    ){\n        constructor(displayName: String, vararg featureFlags: String) : this(\n            displayName,\n            featureFlags.toList()\n        )\n\n        constructor(displayName: String, singleFeature: String) : this(\n            displayName,\n            listOf(singleFeature)\n        )\n    }\n\n    /**\n     * List of all possible feature flags.\n     * CHRONOLOGY IS IMPORTANT. Elements are arranged in accordance of device release date.\n     */\n    val allFeatures = listOf(\n\n        Features(\"Pixel 2016\", // Pixel XL\n            \"com.google.android.apps.photos.NEXUS_PRELOAD\",\n            \"com.google.android.apps.photos.nexus_preload\",\n            \"com.google.android.feature.PIXEL_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_PRELOAD\",\n            \"com.google.android.apps.photos.PIXEL_2016_PRELOAD\",\n        ),\n\n        Features(\"Pixel 2017\", // Pixel 2\n            \"com.google.android.feature.PIXEL_2017_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2017_PRELOAD\"\n        ),\n\n        Features(\"Pixel 2018\", // Pixel 3 XL\n            \"com.google.android.feature.PIXEL_2018_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2018_PRELOAD\"\n        ),\n\n        Features(\"Pixel 2019 mid-year\", // Pixel 3a XL\n            \"com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD\",\n        ),\n\n        Features(\"Pixel 2019\", // Pixel 4 XL\n            \"com.google.android.feature.PIXEL_2019_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2019_PRELOAD\",\n        ),\n\n        Features(\"Pixel 2020 mid-year\", // Pixel 4a\n            \"com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2020_MIDYEAR_PRELOAD\",\n        ),\n\n        Features(\"Pixel 2020\", // Pixel 5\n            \"com.google.android.feature.PIXEL_2020_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2020_PRELOAD\",\n        ),\n\n        Features(\"Pixel 2021 mid-year\", // Pixel 5a\n            \"com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2021_MIDYEAR_PRELOAD\",\n        ),\n\n        Features(\"Pixel 2021\", // Pixel 6 Pro\n            \"com.google.android.feature.PIXEL_2021_EXPERIENCE\",\n            \"com.google.android.apps.photos.PIXEL_2021_PRELOAD\",\n        ),\n    )\n\n    /**\n     * Example if [featureLevel] = \"Pixel 2020\", return will have\n     * list of all elements from [allFeatures] from \"Pixel 2016\" i.e. index = 0,\n     * to \"Pixel 2020\" i.e index = 6, both inclusive.\n     */\n    private fun getFeaturesUpTo(featureLevel: String): List<Features> {\n        val allFeatureDisplayNames = allFeatures.map { it.displayName }\n        val levelIndex = allFeatureDisplayNames.indexOf(featureLevel)\n        return if (levelIndex == -1) listOf()\n        else {\n            allFeatures.withIndex().filter { it.index <= levelIndex }.map { it.value }\n        }\n    }\n\n    /**\n     * Class storing android version information to be faked.\n     *\n     * @param label Just a string to show the user. Not spoofed.\n     * @param release Corresponds to `ro.build.version.release`. Example values: \"12\", \"11\", \"8.1.0\" etc.\n     * @param sdk Corresponds to `ro.build.version.sdk`.\n     */\n    data class AndroidVersion(\n        val label: String,\n        val release: String,\n        val sdk: Int,\n    ){\n        fun getAsMap() = hashMapOf(\n            Pair(\"RELEASE\", release),\n            Pair(\"SDK_INT\", sdk),\n            Pair(\"SDK\", sdk.toString()),\n        )\n    }\n\n    /**\n     * List of all major android versions.\n     * Pixel 1 series launched with nougat, so that is the lowest version.\n     */\n    val allAndroidVersions = listOf(\n        AndroidVersion(\"Nougat 7.1.2\", \"7.1.2\", 25),\n        AndroidVersion(\"Oreo 8.1.0\", \"8.1.0\", 27),\n        AndroidVersion(\"Pie 9.0\", \"9\", 28),\n        AndroidVersion(\"Q 10.0\", \"10\", 29),\n        AndroidVersion(\"R 11.0\", \"11\", 30),\n        AndroidVersion(\"S 12.0\", \"12\", 31),\n    )\n\n    /**\n     * Get instance of [AndroidVersion] from specified [label].\n     * Send null if no such label.\n     */\n    fun getAndroidVersionFromLabel(label: String) = allAndroidVersions.find { it.label == label }\n\n    /**\n     * Class to contain device names and their respective build properties.\n     * @param deviceName Actual device names, example \"Pixel 4a\".\n     * @param props Contains the device properties to spoof.\n     * @param featureLevelName Points to the features expected to be spoofed from [allFeatures],\n     * from \"Pixel 2016\" up to this level.\n     */\n    data class DeviceEntries(\n        val deviceName: String,\n        val props: HashMap<String, String>,\n        val featureLevelName: String,\n        val androidVersion: AndroidVersion?,\n    )\n\n    /**\n     * List of all devices and their build props and their required feature levels.\n     */\n    val allDevices = listOf(\n\n        DeviceEntries(\"None\", hashMapOf(), \"None\", null),\n\n        DeviceEntries(\n            \"Pixel XL\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"marlin\"),\n                Pair(\"PRODUCT\", \"marlin\"),\n                Pair(\"MODEL\", \"Pixel XL\"),\n                Pair(\"FINGERPRINT\", \"google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys\"),\n            ),\n            \"Pixel 2016\",\n            getAndroidVersionFromLabel(\"Q 10.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 2\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"walleye\"),\n                Pair(\"PRODUCT\", \"walleye\"),\n                Pair(\"MODEL\", \"Pixel 2\"),\n                Pair(\"FINGERPRINT\", \"google/walleye/walleye:8.1.0/OPM1.171019.021/4565141:user/release-keys\"),\n            ),\n            \"Pixel 2017\",\n            getAndroidVersionFromLabel(\"Oreo 8.1.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 3 XL\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"crosshatch\"),\n                Pair(\"PRODUCT\", \"crosshatch\"),\n                Pair(\"MODEL\", \"Pixel 3 XL\"),\n                Pair(\"FINGERPRINT\", \"google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys\"),\n            ),\n            \"Pixel 2018\",\n            getAndroidVersionFromLabel(\"R 11.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 3a XL\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"bonito\"),\n                Pair(\"PRODUCT\", \"bonito\"),\n                Pair(\"MODEL\", \"Pixel 3a XL\"),\n                Pair(\"FINGERPRINT\", \"google/bonito/bonito:11/RQ3A.211001.001/7641976:user/release-keys\"),\n            ),\n            \"Pixel 2019 mid-year\",\n            getAndroidVersionFromLabel(\"R 11.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 4 XL\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"coral\"),\n                Pair(\"PRODUCT\", \"coral\"),\n                Pair(\"MODEL\", \"Pixel 4 XL\"),\n                Pair(\"FINGERPRINT\", \"google/coral/coral:12/SP1A.211105.002/7743617:user/release-keys\"),\n            ),\n            \"Pixel 2019\",\n            getAndroidVersionFromLabel(\"S 12.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 4a\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"sunfish\"),\n                Pair(\"PRODUCT\", \"sunfish\"),\n                Pair(\"MODEL\", \"Pixel 4a\"),\n                Pair(\"FINGERPRINT\", \"google/sunfish/sunfish:11/RQ3A.211001.001/7641976:user/release-keys\"),\n            ),\n            \"Pixel 2020 mid-year\",\n            getAndroidVersionFromLabel(\"R 11.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 5\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"redfin\"),\n                Pair(\"PRODUCT\", \"redfin\"),\n                Pair(\"MODEL\", \"Pixel 5\"),\n                Pair(\"FINGERPRINT\", \"google/redfin/redfin:12/SP1A.211105.003/7757856:user/release-keys\"),\n            ),\n            \"Pixel 2020\",\n            getAndroidVersionFromLabel(\"S 12.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 5a\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"barbet\"),\n                Pair(\"PRODUCT\", \"barbet\"),\n                Pair(\"MODEL\", \"Pixel 5a\"),\n                Pair(\"FINGERPRINT\", \"google/barbet/barbet:11/RD2A.211001.002/7644766:user/release-keys\"),\n            ),\n            \"Pixel 2021 mid-year\",\n            getAndroidVersionFromLabel(\"R 11.0\"),\n        ),\n\n        DeviceEntries(\n            \"Pixel 6 Pro\", hashMapOf(\n                Pair(\"BRAND\", \"google\"),\n                Pair(\"MANUFACTURER\", \"Google\"),\n                Pair(\"DEVICE\", \"raven\"),\n                Pair(\"PRODUCT\", \"raven\"),\n                Pair(\"MODEL\", \"Pixel 6 Pro\"),\n                Pair(\"FINGERPRINT\", \"google/raven/raven:12/SD1A.210817.036/7805805:user/release-keys\"),\n            ),\n            \"Pixel 2021\",\n            getAndroidVersionFromLabel(\"S 12.0\"),\n        ),\n    )\n\n    /**\n     * Get instance of [DeviceEntries] from a supplied [deviceName].\n     */\n    fun getDeviceProps(deviceName: String?) = allDevices.find { it.deviceName == deviceName }\n\n    /**\n     * Call [getFeaturesUpTo] using a device name rather than feature level.\n     * Used in spinner in main activity.\n     */\n    fun getFeaturesUpToFromDeviceName(deviceName: String?): Set<String>{\n        return getDeviceProps(deviceName)?.let {\n            getFeaturesUpTo(it.featureLevelName).map { it.displayName }.toSet()\n        }?: setOf()\n    }\n\n    /**\n     * Default name of device to spoof.\n     */\n    val defaultDeviceName = \"Pixel 5\"\n\n    /**\n     * Default feature level to spoof up to. Corresponds to what is expected for the device in [defaultDeviceName].\n     */\n    val defaultFeatures = getFeaturesUpTo(\"Pixel 2020\")\n\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/DeviceSpoofer.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nimport android.os.Build\nimport android.util.Log\nimport balti.xposed.pixelifygooglephotos.Constants.PACKAGE_NAME_GOOGLE_PHOTOS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_DEVICE_TO_SPOOF\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_ENABLE_VERBOSE_LOGS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_MANUAL\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_STRICTLY_CHECK_GOOGLE_PHOTOS\nimport de.robv.android.xposed.IXposedHookLoadPackage\nimport de.robv.android.xposed.XSharedPreferences\nimport de.robv.android.xposed.XposedBridge\nimport de.robv.android.xposed.XposedHelpers\nimport de.robv.android.xposed.callbacks.XC_LoadPackage\n\n/**\n * Codenames of pixels:\n * https://oneandroid.net/all-google-pixel-codenames-from-sailfish-to-redfin/\n *\n * Device properties stored in [DeviceProps].\n */\nclass DeviceSpoofer: IXposedHookLoadPackage {\n\n    /**\n     * Simple message to log messages in lsposed log as well as android log.\n     */\n    private fun log(message: String){\n        XposedBridge.log(\"PixelifyGooglePhotos: $message\")\n        Log.d(\"PixelifyGooglePhotos\", message)\n    }\n\n    /**\n     * To read preference of user.\n     */\n    private val pref by lazy {\n        XSharedPreferences(BuildConfig.APPLICATION_ID, Constants.SHARED_PREF_FILE_NAME)\n    }\n\n    private val verboseLog: Boolean by lazy {\n        pref.getBoolean(PREF_ENABLE_VERBOSE_LOGS, false)\n    }\n\n    /**\n     * This will always be null if the user has not chosen to spoof android version.\n     * If not null, then following will be spoofed:\n     * [Build.VERSION.RELEASE], [Build.VERSION.SDK_INT]\n     *\n     * @see DeviceProps.AndroidVersion\n     */\n    private val androidVersionToSpoof: DeviceProps.AndroidVersion? by lazy {\n        if (pref.getBoolean(PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE, false))\n            finalDeviceToSpoof?.androidVersion\n        else {\n            pref.getString(PREF_SPOOF_ANDROID_VERSION_MANUAL, null)?.let {\n                DeviceProps.getAndroidVersionFromLabel(it)\n            }\n        }\n    }\n\n    /**\n     * This is the final device to spoof.\n     * By default use Pixel 5.\n     */\n    private val finalDeviceToSpoof by lazy {\n        val deviceName = pref.getString(PREF_DEVICE_TO_SPOOF, DeviceProps.defaultDeviceName)\n        log(\"Device spoof: $deviceName\")\n        DeviceProps.getDeviceProps(deviceName)\n    }\n\n    /**\n     * Inspired by:\n     * https://github.com/itsuki-t/FakeDeviceData/blob/master/src/jp/rmitkt/xposed/fakedevicedata/FakeDeviceData.java\n     */\n    override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n\n        /**\n         * If user selects to never use this on any other app other than Google photos,\n         * then check package name and return if necessary.\n         */\n        if (pref.getBoolean(PREF_STRICTLY_CHECK_GOOGLE_PHOTOS, true) &&\n            lpparam?.packageName != PACKAGE_NAME_GOOGLE_PHOTOS) return\n\n        log(\"Loaded DeviceSpoofer for ${lpparam?.packageName}\")\n        log(\"Device spoof: ${finalDeviceToSpoof?.deviceName}\")\n\n        finalDeviceToSpoof?.props?.run {\n\n            if (keys.isEmpty()) return\n            val classLoader = lpparam?.classLoader ?: return\n\n            val classBuild = XposedHelpers.findClass(\"android.os.Build\", classLoader)\n            keys.forEach {\n                XposedHelpers.setStaticObjectField(classBuild, it, this[it])\n                if (verboseLog) log(\"DEVICE PROPS: $it - ${this[it]}\")\n            }\n\n        }\n\n        androidVersionToSpoof?.getAsMap()?.run {\n\n            val classLoader = lpparam?.classLoader ?: return\n            val classBuild = XposedHelpers.findClass(\"android.os.Build.VERSION\", classLoader)\n\n            keys.forEach {\n                XposedHelpers.setStaticObjectField(classBuild, it, this[it])\n                if (verboseLog) log(\"VERSION SPOOF: $it - ${this[it]}\")\n            }\n        }\n\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/FeatureCustomize.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.widget.Button\nimport android.widget.CheckBox\nimport android.widget.LinearLayout\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.children\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_FEATURES_LIST\nimport balti.xposed.pixelifygooglephotos.Constants.SHARED_PREF_FILE_NAME\n\n/**\n * Provides granular controls for selecting some specific feature flags.\n */\nclass FeatureCustomize: AppCompatActivity(R.layout.feature_customize) {\n\n    private val pref by lazy {\n        getSharedPreferences(SHARED_PREF_FILE_NAME, MODE_WORLD_READABLE)\n    }\n\n    /**\n     * Set of feature names enabled by the user, fetched from shared prefs.\n     * If nothing is enabled, use default case from [DeviceProps.defaultFeatures].\n     */\n    private val enabledFeaturesNames: Set<String> by lazy {\n\n        val defaultFeatures = DeviceProps.defaultFeatures\n        val defaultFeatureLevelsName = defaultFeatures.map { it.displayName }.toSet()\n\n        pref.getStringSet(PREF_SPOOF_FEATURES_LIST, defaultFeatureLevelsName)?: setOf()\n    }\n\n    private val utils by lazy { Utils() }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val checkboxHolder = findViewById<LinearLayout>(R.id.feature_checkbox_holder)\n        val saveButton = findViewById<Button>(R.id.save_features)\n\n        /**\n         * Populate checkboxes in for each feature name.\n         */\n        DeviceProps.allFeatures.withIndex().forEach {\n\n            val checkBox = CheckBox(this)\n            checkBox.apply {\n                text = it.value.displayName\n                id = it.index + 1\n                isChecked = text in enabledFeaturesNames\n            }\n\n            checkboxHolder.addView(checkBox)\n\n        }\n\n        /**\n         * When \"Save\" button is pressed, store the selected checkbox values in shared prefs.\n         * Then close the activity and send RESULT_OK\n         */\n        saveButton.setOnClickListener {\n\n            val checkedFeatureNames =\n                checkboxHolder.children.filter { it is CheckBox && it.isChecked }\n                    .map { (it as CheckBox).text.toString() }.toSet()\n\n            pref.edit().apply {\n                putStringSet(PREF_SPOOF_FEATURES_LIST, checkedFeatureNames)\n                apply()\n            }\n\n            setResult(Activity.RESULT_OK)\n            finish()\n\n        }\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/FeatureSpoofer.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nimport android.util.Log\nimport balti.xposed.pixelifygooglephotos.Constants.PACKAGE_NAME_GOOGLE_PHOTOS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_ENABLE_VERBOSE_LOGS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_OVERRIDE_ROM_FEATURE_LEVELS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_FEATURES_LIST\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_STRICTLY_CHECK_GOOGLE_PHOTOS\nimport balti.xposed.pixelifygooglephotos.Constants.SHARED_PREF_FILE_NAME\nimport de.robv.android.xposed.*\nimport de.robv.android.xposed.callbacks.XC_LoadPackage\n\nclass FeatureSpoofer: IXposedHookLoadPackage {\n\n    /**\n     * Actual class is not android.content.pm.PackageManager.\n     * It is an abstract class which cannot be hooked.\n     * Actual class found from stackoverflow:\n     * https://stackoverflow.com/questions/66523720/xposed-cant-hook-getinstalledapplications\n     */\n    private val CLASS_APPLICATION_MANAGER = \"android.app.ApplicationPackageManager\"\n\n    /**\n     * Method hasSystemFeature(). Two signatures exist. We need to hook both.\n     * https://developer.android.com/reference/android/content/pm/PackageManager#hasSystemFeature(java.lang.String)\n     * https://developer.android.com/reference/android/content/pm/PackageManager#hasSystemFeature(java.lang.String,%20int)\n     */\n    private val METHOD_HAS_SYSTEM_FEATURE = \"hasSystemFeature\"\n\n    /**\n     * Simple message to log messages in lsposed log as well as android log.\n     */\n    private fun log(message: String){\n        XposedBridge.log(\"PixelifyGooglePhotos: $message\")\n        Log.d(\"PixelifyGooglePhotos\", message)\n    }\n\n    /**\n     * To read preference of user.\n     */\n    private val pref by lazy {\n        XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREF_FILE_NAME).apply {\n            log(\"Preference location: ${file.canonicalPath}\")\n        }\n    }\n\n    private val verboseLog: Boolean by lazy {\n        pref.getBoolean(PREF_ENABLE_VERBOSE_LOGS, false)\n    }\n\n    /**\n     * This is the final list of features to spoof.\n     * Gets the specific set of features to be enabled selected by the user.\n     * Default case: uses all features from \"Pixel 2016\" to \"Pixel 2020\".\n     */\n    private val finalFeaturesToSpoof: List<String> by lazy {\n\n        val defaultFeatures = DeviceProps.defaultFeatures\n        val defaultFeatureLevelsName = defaultFeatures.map { it.displayName }.toSet()\n\n        val featureFlags = pref.getStringSet(PREF_SPOOF_FEATURES_LIST, defaultFeatureLevelsName)?.let { set ->\n\n            val eligibleFeatures: List<DeviceProps.Features> =\n\n                when {\n                    set.isEmpty() -> {\n                        log(\"Feature flags init: EMPTY SET\")\n                        listOf()\n                    }\n                    set == defaultFeatureLevelsName -> {\n                        log(\"Feature flags init: DEFAULT SET\")\n                        defaultFeatures\n                    }\n                    else -> DeviceProps.allFeatures.filter { set.contains(it.displayName) }\n                }\n\n            val allFeatureFlags = ArrayList<String>(0)\n\n            eligibleFeatures.forEach {\n                allFeatureFlags.addAll(it.featureFlags)\n            }\n\n            allFeatureFlags\n        }?: listOf()\n\n        featureFlags.apply {\n            log(\"Pass TRUE for feature flags: $featureFlags\")\n        }\n    }\n\n    /**\n     * Preference to override upper feature levels from custom ROMs\n     */\n    private val overrideCustomROMLevels by lazy {\n        pref.getBoolean(PREF_OVERRIDE_ROM_FEATURE_LEVELS, true)\n    }\n\n    /**\n     * List of feature flags which are not present in [finalFeaturesToSpoof].\n     * If any feature is in this list, spoof it as not present.\n     * Only if preference [PREF_OVERRIDE_ROM_FEATURE_LEVELS] are enabled.\n     */\n    private val featuresNotToSpoof: List<String> by lazy {\n\n        val allFeatureFlags = ArrayList<String>(0)\n\n        DeviceProps.allFeatures.map { it.featureFlags }.forEach {\n            allFeatureFlags.addAll(it)\n        }\n\n        allFeatureFlags.filter { it !in finalFeaturesToSpoof }.apply {\n            log(\"Pass FALSE for feature flags: $this\")\n        }\n    }\n\n    /**\n     * If a feature needed for google photos is needed, i.e. features in [finalFeaturesToSpoof],\n     * then set result of hooked method [METHOD_HAS_SYSTEM_FEATURE] as `true`.\n     * If [PREF_OVERRIDE_ROM_FEATURE_LEVELS] is enabled, and the feature is present in [featuresNotToSpoof]\n     * then set result as `false`.\n     * Else don't set anything.\n     */\n    private fun spoofFeatureEnquiryResultIfNeeded(param: XC_MethodHook.MethodHookParam?){\n        val arguments = param?.args?.toList()\n\n        var passFeatureTrue = false\n        var passFeatureFalse = false\n\n        arguments?.forEach {\n            if (it.toString() in finalFeaturesToSpoof) passFeatureTrue = true\n            else if (overrideCustomROMLevels){\n                if (it.toString() in featuresNotToSpoof) passFeatureFalse = true\n            }\n        }\n\n        if (passFeatureTrue) param?.setResult(true).apply {\n            if (verboseLog) log(\"TRUE - feature args: $arguments\")\n        }\n        else if (passFeatureFalse) param?.setResult(false).apply {\n            if (verboseLog) log(\"FALSE - feature args: $arguments\")\n        }\n        else {\n            if (verboseLog) log(\"NO_CHANGE - feature args: $arguments\")\n        }\n    }\n\n    override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n\n        /**\n         * If user selects to never use this on any other app other than Google photos,\n         * then check package name and return if necessary.\n         */\n        if (pref.getBoolean(PREF_STRICTLY_CHECK_GOOGLE_PHOTOS, true) &&\n            lpparam?.packageName != PACKAGE_NAME_GOOGLE_PHOTOS) return\n\n        log(\"Loaded FeatureSpoofer for ${lpparam?.packageName}\")\n\n        /**\n         * Hook hasSystemFeature(String).\n         */\n        XposedHelpers.findAndHookMethod(\n            CLASS_APPLICATION_MANAGER,\n            lpparam?.classLoader,\n            METHOD_HAS_SYSTEM_FEATURE, String::class.java,\n            object: XC_MethodHook() {\n\n                override fun beforeHookedMethod(param: MethodHookParam?) {\n                    super.beforeHookedMethod(param)\n                    spoofFeatureEnquiryResultIfNeeded(param)\n                }\n\n            }\n        )\n\n        /**\n         * Hook hasSystemFeature(String, int).\n         */\n        XposedHelpers.findAndHookMethod(\n            CLASS_APPLICATION_MANAGER,\n            lpparam?.classLoader,\n            METHOD_HAS_SYSTEM_FEATURE, String::class.java, Int::class.java,\n            object: XC_MethodHook() {\n\n                override fun beforeHookedMethod(param: MethodHookParam?) {\n                    super.beforeHookedMethod(param)\n                    spoofFeatureEnquiryResultIfNeeded(param)\n                }\n\n            }\n        )\n\n    }\n}"
  },
  {
    "path": "app/src/main/java/balti/xposed/pixelifygooglephotos/Utils.kt",
    "content": "package balti.xposed.pixelifygooglephotos\n\nimport android.content.Context\nimport android.content.Intent\nimport android.content.SharedPreferences\nimport android.net.Uri\nimport android.provider.Settings\nimport android.widget.Toast\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_DEVICE_TO_SPOOF\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_ENABLE_VERBOSE_LOGS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_LAST_VERSION\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_OVERRIDE_ROM_FEATURE_LEVELS\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_ANDROID_VERSION_MANUAL\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_SPOOF_FEATURES_LIST\nimport balti.xposed.pixelifygooglephotos.Constants.PREF_STRICTLY_CHECK_GOOGLE_PHOTOS\nimport org.json.JSONArray\nimport org.json.JSONObject\nimport java.io.BufferedWriter\nimport java.io.ByteArrayOutputStream\nimport java.io.OutputStreamWriter\n\n\n/**\n * Utilities class for various functions.\n */\nclass Utils {\n\n    /**\n     * Used to force close an app.\n     *\n     * Uses root to stop an application.\n     *\n     * Tried my level best to use xposed API to force stop the application,\n     * but it kept throwing error that XposedHelpers not found. No idea why.\n     */\n    fun forceStopPackage(packageName: String, context: Context){\n        try {\n            Toast.makeText(context, R.string.killing_please_wait, Toast.LENGTH_SHORT).show()\n            Runtime.getRuntime().exec(\"su\").apply {\n                BufferedWriter(OutputStreamWriter(this.outputStream)).run {\n                    this.write(\"am force-stop $packageName\\n\")\n                    this.write(\"exit\\n\")\n                    this.flush()\n                }\n            }\n        } catch (e: Exception){\n            Toast.makeText(context, R.string.failed_to_stop_package, Toast.LENGTH_SHORT).show()\n            val intent = Intent().apply {\n                action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS\n                data = Uri.fromParts(\"package\", packageName, null)\n            }\n            context.startActivity(intent)\n        }\n\n        /*\n        * This could should have theoretically worked:\n        *\n        * val activityManager: ActivityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager\n        * XposedHelpers.callMethod(activityManager, \"forceStopPackage\", packageName)\n        */\n    }\n\n    /**\n     * Launch an app.\n     */\n    fun openApplication(packageName: String, context: Context){\n        try {\n            val pm = context.packageManager\n            val launchIntent = pm.getLaunchIntentForPackage(packageName)\n            if (launchIntent != null) {\n                context.startActivity(launchIntent)\n            }\n        }\n        catch (e: Exception){\n            Toast.makeText(context, R.string.failed_to_launch_package, Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    /**\n     * Change permissions on private data, shared_prefs directory and preferences file.\n     * Otherwise XSharedPreference cannot read the file.\n     * Solution inspired from:\n     * https://github.com/rovo89/XposedBridge/issues/233\n     * https://github.com/GravityBox/GravityBox/blob/0aec21792c218a48602a258fbb0ab1fcb1e9be0c/GravityBox/src/main/java/com/ceco/r/gravitybox/WorldReadablePrefs.java\n     */\n    /*@SuppressLint(\"SetWorldReadable\")\n    fun fixPermissions(thisPackageName: String) {\n        val dataDirectory = File(\"/data/data/$thisPackageName\")\n        dataDirectory.apply {\n            setExecutable(true, false)\n            setReadable(true, false)\n        }\n        val sharedPrefsFolder = File(dataDirectory, \"shared_prefs\")\n        sharedPrefsFolder.apply {\n            if (exists()){\n                setExecutable(true, false)\n                setReadable(true, false)\n            }\n        }\n        val prefsFile = File(sharedPrefsFolder, \"${Constants.SHARED_PREF_FILE_NAME}.xml\")\n        prefsFile.apply {\n            if (exists()) setReadable(true, false)\n        }\n    }*/\n\n    /**\n     * Write all keys of shared preference in a file as a JSON string.\n     *\n     * @param context Activity context\n     * @param uri Uri of file to write to.\n     * Using uri as it can be used to write a file in internal cache directory,\n     * as well as an external location opened using [Intent.ACTION_CREATE_DOCUMENT].\n     * @param pref SharedPreference instance.\n     */\n    fun writeConfigFile(context: Context, uri: Uri, pref: SharedPreferences?) {\n\n        // List of keys from shared preference which need not be copied to file.\n        // Or copied later like PREF_SPOOF_FEATURES_LIST.\n        val fieldsNotToCopy = listOf(PREF_LAST_VERSION, PREF_SPOOF_FEATURES_LIST)\n\n        val outputStream = context.contentResolver.openOutputStream(uri)\n        val writer = BufferedWriter(OutputStreamWriter(outputStream))\n\n        val jsonObject = JSONObject()\n        pref?.all?.let { allPrefs ->\n            for (key in allPrefs.keys){\n                if (key !in fieldsNotToCopy) jsonObject.put(key, allPrefs[key])\n            }\n        }\n\n        // Store PREF_SPOOF_FEATURES_LIST\n        pref?.getStringSet(PREF_SPOOF_FEATURES_LIST, setOf())?.let {\n            jsonObject.put(PREF_SPOOF_FEATURES_LIST, JSONArray(it.toTypedArray()))\n        }\n\n        writer.run {\n            write(jsonObject.toString(4))\n            close()\n        }\n    }\n\n    /**\n     * Read an exported JSON file and stores entries in shared preference.\n     *\n     * @param context Activity context\n     * @param uri Uri of file to read from.\n     * @param pref SharedPreference instance.\n     */\n    fun readConfigFile(context: Context, uri: Uri, pref: SharedPreferences?) {\n\n        var jsonObject = JSONObject()\n        val baos = ByteArrayOutputStream()\n\n        val inputStream = context.contentResolver.openInputStream(uri)\n        inputStream?.use { input ->\n            baos.use { output ->\n                input.copyTo(output)\n            }\n            jsonObject = JSONObject(baos.toString())\n        }\n\n        /**\n         * In inbuilt function exists to convert JSONArray to List.\n         */\n        fun convertJsonArrayToList(jsonArray: JSONArray): List<String> {\n            val list = ArrayList<String>()\n            for (i in 0 until jsonArray.length()) {\n                list.add(jsonArray[i].toString())\n            }\n            return list\n        }\n\n        /**\n         * Check for field and store in shared prefs.\n         */\n        pref?.edit()?.apply {\n\n            PREF_SPOOF_FEATURES_LIST.let { key ->\n                jsonObject.optJSONArray(key)?.let {\n                    putStringSet(key, convertJsonArrayToList(it).toSet())\n                }\n            }\n\n            PREF_DEVICE_TO_SPOOF.let { key ->\n                jsonObject.optString(key)?.let {\n                    putString(key, it)\n                }\n            }\n\n            PREF_STRICTLY_CHECK_GOOGLE_PHOTOS.let { key ->\n                jsonObject.optBoolean(key, true).let {\n                    putBoolean(key, it)\n                }\n            }\n\n            PREF_OVERRIDE_ROM_FEATURE_LEVELS.let { key ->\n                jsonObject.optBoolean(key, true).let {\n                    putBoolean(key, it)\n                }\n            }\n\n            /** Advanced options */\n\n            PREF_ENABLE_VERBOSE_LOGS.let { key ->\n                jsonObject.optBoolean(key, true).let {\n                    putBoolean(key, it)\n                }\n            }\n\n            PREF_SPOOF_ANDROID_VERSION_FOLLOW_DEVICE.let { key ->\n                jsonObject.optBoolean(key, true).let {\n                    putBoolean(key, it)\n                }\n            }\n\n            PREF_SPOOF_ANDROID_VERSION_MANUAL.let { key ->\n                jsonObject.optString(key)?.let {\n                    putString(key, it)\n                }\n            }\n\n            apply()\n        }\n\n    }\n\n}"
  },
  {
    "path": "app/src/main/res/drawable/ic_export.xml",
    "content": "<vector android:height=\"30dp\" android:tint=\"#8F8F8F\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"30dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M9,16h6v-6h4l-7,-7 -7,7h4zM5,18h14v2L5,20z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_import.xml",
    "content": "<vector android:height=\"30dp\" android:tint=\"#8F8F8F\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"30dp\" 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_info.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\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_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    android:height=\"108dp\"\n    android:width=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#3DDC84\"\n          android:pathData=\"M0,0h108v108h-108z\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M9,0L9,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,0L19,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M89,0L89,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M99,0L99,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,9L108,9\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,19L108,19\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,89L108,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,99L108,99\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,29L89,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,39L89,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,49L89,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,59L89,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,69L89,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,79L89,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,19L29,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,19L39,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,19L49,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,19L59,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,19L69,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,19L79,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_open.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\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,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n  <group android:scaleX=\"0.21304688\"\n      android:scaleY=\"0.21304688\"\n      android:translateX=\"-0.54\"\n      android:translateY=\"-0.54\">\n    <group>\n      <clip-path\n          android:pathData=\"M0,0h512v512h-512z\"/>\n      <path\n          android:pathData=\"M-8.666,-5.488h531.415v522.876h-531.415z\"\n          android:fillColor=\"#182A39\"/>\n      <path\n          android:pathData=\"M-52.692,569.148C109.224,109.431 274.689,109.451 443.637,569.148\">\n        <aapt:attr name=\"android:fillColor\">\n          <gradient \n              android:startY=\"387.07437\"\n              android:startX=\"45.65521\"\n              android:endY=\"239.52113\"\n              android:endX=\"278.26874\"\n              android:type=\"linear\">\n            <item android:offset=\"0.008333334\" android:color=\"#FF2E5EAC\"/>\n            <item android:offset=\"0.9875\" android:color=\"#FF4285F4\"/>\n          </gradient>\n        </aapt:attr>\n      </path>\n      <path\n          android:pathData=\"M88.387,565.081C238.659,154.603 392.224,154.621 549.022,565.081\">\n        <aapt:attr name=\"android:fillColor\">\n          <gradient \n              android:startY=\"440.2372\"\n              android:startX=\"251.56363\"\n              android:endY=\"336.55188\"\n              android:endX=\"438.53683\"\n              android:type=\"linear\">\n            <item android:offset=\"0\" android:color=\"#FF195128\"/>\n            <item android:offset=\"1\" android:color=\"#FF34A853\"/>\n          </gradient>\n        </aapt:attr>\n      </path>\n      <path\n          android:pathData=\"M276.276,170.813a40.61,39.957 0,1 0,81.219 0a40.61,39.957 0,1 0,-81.219 0z\"\n          android:fillColor=\"#FFFFFF\"/>\n      <path\n          android:pathData=\"M279.024,343.333L279.011,337.069C275.249,335.713 275.239,335.71 271.478,334.353L267.596,339.213C265.088,339.345 263.82,339.411 261.311,339.543L256.944,335.116C253.344,336.859 253.335,336.864 249.736,338.606L250.376,344.837C248.697,346.733 247.848,347.691 246.169,349.587L240.005,349.591C238.676,353.412 238.673,353.422 237.344,357.242L242.131,361.194C242.265,363.744 242.332,365.032 242.466,367.582L238.117,372.014C239.837,375.675 239.841,375.684 241.562,379.345L247.692,378.703C249.56,380.413 250.504,381.277 252.372,382.986L252.385,389.25C256.147,390.606 256.157,390.61 259.918,391.966L263.8,387.106C266.309,386.975 267.576,386.908 270.085,386.776L274.452,391.203C278.052,389.46 278.061,389.456 281.661,387.713L281.02,381.482C282.699,379.586 283.548,378.628 285.228,376.732L291.391,376.728C292.72,372.908 292.723,372.898 294.052,369.077L289.265,365.125C289.131,362.575 289.064,361.287 288.93,358.737L293.279,354.306C291.559,350.645 291.555,350.635 289.834,346.975L283.704,347.617C281.836,345.907 280.892,345.043 279.024,343.333ZM272.055,368.977C268.898,372.54 263.494,372.824 259.983,369.611C256.473,366.398 256.185,360.905 259.342,357.343C262.498,353.78 267.902,353.496 271.413,356.709C274.923,359.921 275.211,365.414 272.055,368.977Z\"\n          android:fillColor=\"#FFD600\"/>\n      <path\n          android:pathData=\"M253.958,352.416C248.139,358.986 248.67,369.15 255.144,375.074C261.617,380.998 271.618,380.473 277.438,373.904C283.257,367.334 282.726,357.17 276.252,351.246C269.779,345.321 259.778,345.846 253.958,352.416ZM275.292,371.94C270.536,377.309 262.363,377.739 257.072,372.897C251.782,368.055 251.348,359.748 256.104,354.379C260.86,349.01 269.033,348.581 274.323,353.423C279.614,358.264 280.049,366.571 275.292,371.94Z\"\n          android:fillColor=\"#FBBC04\"/>\n      <path\n          android:pathData=\"M258.269,356.361C254.586,360.518 254.923,366.951 259.019,370.699C263.115,374.448 269.444,374.116 273.127,369.959C276.81,365.801 276.473,359.369 272.377,355.62C268.28,351.871 261.952,352.203 258.269,356.361ZM270.982,367.995C268.362,370.952 263.861,371.189 260.948,368.522C258.034,365.856 257.795,361.281 260.414,358.324C263.033,355.367 267.535,355.131 270.448,357.797C273.362,360.464 273.601,365.039 270.982,367.995Z\"\n          android:fillColor=\"#FBBC04\"/>\n      <path\n          android:pathData=\"M300.153,311.884L298.525,307.928C295.799,308.075 295.792,308.075 293.067,308.222L291.873,312.331C290.323,313.084 289.54,313.464 287.99,314.218L284.088,312.586C282.266,314.648 282.261,314.654 280.439,316.717L282.455,320.484C281.885,322.131 281.597,322.963 281.027,324.611L277.136,326.259C277.285,329.029 277.285,329.036 277.434,331.806L281.479,333.026C282.222,334.602 282.598,335.398 283.341,336.975L281.741,340.938C283.774,342.792 283.779,342.797 285.812,344.652L289.516,342.609C291.138,343.191 291.957,343.485 293.579,344.067L295.206,348.023C297.932,347.876 297.939,347.876 300.664,347.729L301.859,343.62C303.408,342.867 304.191,342.486 305.741,341.733L309.643,343.365C311.465,341.302 311.47,341.297 313.292,339.234L311.276,335.466C311.846,333.819 312.135,332.987 312.705,331.34L316.595,329.692C316.446,326.922 316.446,326.915 316.297,324.145L312.252,322.925C311.509,321.349 311.133,320.552 310.39,318.976L311.99,315.013C309.957,313.159 309.952,313.154 307.919,311.299L304.215,313.342C302.594,312.76 301.774,312.466 300.153,311.884ZM302.383,329.955C301.311,333.05 297.973,334.673 294.926,333.579C291.878,332.486 290.277,329.09 291.348,325.995C292.42,322.9 295.758,321.278 298.806,322.371C301.853,323.465 303.454,326.86 302.383,329.955Z\"\n          android:fillColor=\"#F44336\"/>\n      <path\n          android:pathData=\"M286.676,324.319C284.7,330.026 287.664,336.309 293.283,338.325C298.901,340.342 305.08,337.339 307.055,331.632C309.031,325.925 306.067,319.642 300.448,317.626C294.83,315.609 288.652,318.612 286.676,324.319ZM305.193,330.964C303.579,335.628 298.529,338.082 293.937,336.434C289.346,334.786 286.923,329.651 288.538,324.987C290.153,320.323 295.202,317.869 299.794,319.517C304.385,321.165 306.808,326.3 305.193,330.964Z\"\n          android:fillColor=\"#E53935\"/>\n      <path\n          android:pathData=\"M290.417,325.661C289.167,329.273 291.043,333.249 294.598,334.525C298.154,335.801 302.064,333.901 303.314,330.29C304.564,326.678 302.688,322.702 299.133,321.426C295.577,320.15 291.668,322.05 290.417,325.661ZM301.452,329.621C300.563,332.19 297.782,333.541 295.253,332.634C292.724,331.726 291.39,328.898 292.279,326.33C293.169,323.761 295.949,322.41 298.478,323.317C301.007,324.225 302.341,327.053 301.452,329.621Z\"\n          android:fillColor=\"#E53905\"/>\n    </group>\n  </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    android:layout_margin=\"25dp\"\n    android:id=\"@+id/root_view_for_snackbar\"\n    >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:gravity=\"center\"\n        >\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            android:gravity=\"center_vertical\"\n            >\n\n            <ImageButton\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_export\"\n                android:backgroundTint=\"@android:color/white\"\n                android:contentDescription=\"@string/export_config\"\n                android:id=\"@+id/conf_export\"\n                />\n\n            <ImageButton\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_import\"\n                android:backgroundTint=\"@android:color/white\"\n                android:contentDescription=\"@string/import_config\"\n                android:id=\"@+id/conf_import\"\n                />\n\n            <Space\n                android:layout_width=\"0dp\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"/>\n\n            <Button\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/reset\"\n                android:id=\"@+id/reset_settings\"\n                />\n\n        </LinearLayout>\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            android:gravity=\"center_vertical\"\n            >\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/device_to_spoof\"\n                    android:textAppearance=\"?android:attr/textAppearanceLarge\"/>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/spoofs_build_and_features\"/>\n\n            </LinearLayout>\n\n            <Spinner\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:id=\"@+id/device_spoofer_spinner\"/>\n\n        </LinearLayout>\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:paddingTop=\"10dp\"\n            android:foreground=\"?android:attr/selectableItemBackground\"\n            android:id=\"@+id/customize_feature_flags\"\n            >\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/customize_feature_flags\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/feature_flags_changed\"\n                android:textColor=\"?android:attr/colorPrimary\"\n                android:alpha=\"0.0\"\n                android:id=\"@+id/feature_flags_changed\"\n                />\n\n        </LinearLayout>\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <androidx.appcompat.widget.SwitchCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/override_rom_feature_levels\"\n            android:textAppearance=\"?android:attr/textAppearanceLarge\"\n            android:id=\"@+id/override_rom_feature_levels\"/>\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/override_rom_feature_levels_desc\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"/>\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <androidx.appcompat.widget.SwitchCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/spoof_only_in_google_photos\"\n            android:textAppearance=\"?android:attr/textAppearanceLarge\"\n            android:id=\"@+id/spoof_only_in_google_photos_switch\"/>\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/spoof_only_in_google_photos_desc\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"/>\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"15dp\"/>\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/advanced_options\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textStyle=\"bold\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:paddingTop=\"10dp\"\n            android:paddingBottom=\"10dp\"\n            android:foreground=\"?android:attr/selectableItemBackground\"\n            android:id=\"@+id/advanced_options\"\n            />\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"15dp\"/>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/update_available\"\n            android:textColor=\"@color/red\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:textStyle=\"bold\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:foreground=\"?android:attr/selectableItemBackground\"\n            android:visibility=\"gone\"\n            android:id=\"@+id/update_available_link\"\n            />\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"5dp\"/>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/telegram_group\"\n            android:textColor=\"?android:attr/colorPrimary\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:textStyle=\"bold\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:foreground=\"?android:attr/selectableItemBackground\"\n            android:id=\"@+id/telegram_group\"\n            />\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <RelativeLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            >\n\n            <Button\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/force_stop_google_photos\"\n                android:elevation=\"3dp\"\n                android:id=\"@+id/force_stop_google_photos\"\n                />\n\n            <ImageButton\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_toEndOf=\"@id/force_stop_google_photos\"\n                android:layout_alignTop=\"@id/force_stop_google_photos\"\n                android:layout_alignBottom=\"@id/force_stop_google_photos\"\n                android:backgroundTint=\"?android:attr/colorPrimary\"\n                android:contentDescription=\"@string/open_google_photos\"\n                android:src=\"@drawable/ic_open\"\n                android:elevation=\"3dp\"\n                android:id=\"@+id/open_google_photos\"\n                />\n\n        </RelativeLayout>\n\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/advanced_options_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    android:layout_margin=\"25dp\"\n    >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:gravity=\"center\"\n        >\n\n        <CheckBox\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/enable_verbose_logging\"\n            android:id=\"@+id/verbose_logging\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/enable_verbose_logging_desc\"\n            />\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/spoof_android_version\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textColor=\"?android:attr/textColorPrimary\"\n            />\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"10dp\"/>\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/this_might_cause_crash\"\n            android:textColor=\"@color/red\"\n            android:textStyle=\"italic\"\n            />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/spoof_android_version_desc\"\n            />\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"10dp\"/>\n\n        <RadioGroup\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:id=\"@+id/android_version_radio_group\"\n            >\n\n            <RadioButton\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/dont_spoof_android_version\"\n                android:id=\"@+id/dont_spoof_android_version\"\n                />\n\n            <RadioButton\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/follow_spoof_device_version\"\n                android:id=\"@+id/follow_spoof_device_version\"\n                />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\"\n                android:layout_marginStart=\"32dp\"\n                >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:id=\"@+id/device_name_label\"\n                    />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\" - \"\n                    />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:textColor=\"?android:attr/colorAccent\"\n                    android:id=\"@+id/device_android_version\"\n                    />\n\n            </LinearLayout>\n\n            <RadioButton\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/manually_set_android_version\"\n                android:id=\"@+id/manually_set_android_version\"\n                />\n\n            <Spinner\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"32dp\"\n                android:visibility=\"gone\"\n                android:id=\"@+id/android_version_spinner\"\n                />\n\n        </RadioGroup>\n\n        <Space\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"25dp\"/>\n\n        <Button\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/save\"\n            android:layout_gravity=\"center_horizontal\"\n            android:id=\"@+id/save_advanced_option\"\n            />\n\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/feature_customize.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    android:layout_margin=\"25dp\"\n    android:orientation=\"vertical\"\n    >\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\"\n        >\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:layout_gravity=\"center_vertical\"\n            android:id=\"@+id/feature_checkbox_holder\"\n            >\n\n        </LinearLayout>\n\n    </ScrollView>\n\n    <Button\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/save\"\n        android:layout_gravity=\"center_horizontal\"\n        android:id=\"@+id/save_features\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/menu/menu_activity_main.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\n    <item android:id=\"@+id/menu_changelog\"\n        android:icon=\"@drawable/ic_info\"\n        app:showAsAction=\"ifRoom\"\n        android:title=\"@string/changelog\"\n        />\n\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=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_200\">#FFBB86FC</color>\n    <color name=\"purple_500\">#FF6200EE</color>\n    <color name=\"purple_700\">#FF3700B3</color>\n    <color name=\"teal_200\">#FF03DAC5</color>\n    <color name=\"teal_700\">#FF018786</color>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n\n    <color name=\"red\">#C62828</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/module_scope.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"module_scope\">\n        <item>com.google.android.apps.photos</item>\n    </string-array>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"MissingTranslation\">\n\n    <!--Things to update in new versions:\n\n    build.gradle: versionCode\n    build.gradle: versionName\n\n    update_info.json\n\n    changelog\n    -->\n\n    <string name=\"app_name\">Pixelify GPhotos</string>\n\n    <!--activity_main.xml-->\n    <string name=\"reset\">Reset</string>\n    <string name=\"device_to_spoof\">Device to spoof</string>\n    <string name=\"spoofs_build_and_features\">Spoofs build.prop and feature flags.</string>\n    <string name=\"customize_feature_flags\">Customize feature flags.</string>\n    <string name=\"feature_flags_changed\">Feature flags changed!</string>\n    <string name=\"spoof_only_in_google_photos\">Make sure to spoof only in Google Photos.</string>\n    <string name=\"spoof_only_in_google_photos_desc\">In LSPosed, the module is by default only activated for Google Photos.\n    If you want to use it in other apps, then keep this toggle <b>OFF</b>.</string>\n    <string name=\"override_rom_feature_levels\">Override ROM feature levels.</string>\n    <string name=\"override_rom_feature_levels_desc\">Pass false to hooked method for higher feature levels present in ROM\n    if user selects a lower Pixel feature level. <b>(Recommended)</b></string>\n    <string name=\"force_stop_google_photos\">Force stop\\nGoogle Photos</string>\n    <string name=\"module_not_enabled\">\n        \\n\n        Looks like the this app module is not enabled.\\n\n        Please enable it in LSPosed / EdXposed, then reboot.\\n\n        If already enabled, then just reboot. If that does not work, kindly uninstall and reinstall.\n    </string>\n    <string name=\"close\">Close</string>\n    <string name=\"open_google_photos\">Open Google Photos</string>\n    <string name=\"telegram_group\">Telegram group!</string>\n    <string name=\"update_available\">App update available!</string>\n\n    <!--changelog-->\n    <string name=\"version_head\">Version 4.1</string>\n    <string name=\"version_desc\">\n        \\n\n        # In light of recent event of Vanced project getting shut down because of using Youtube logo,\n        this app\\'s logo has been updated to avoid any legal conflict.\\n\n    </string>\n\n    <!--menu_activity_main.xml-->\n    <string name=\"changelog\">Change log</string>\n\n    <!--ActivityMain.xml-->\n    <string name=\"please_force_stop_google_photos\">Please force stop Google Photos to take effect.</string>\n    <string name=\"export_config\">Export configuration</string>\n    <string name=\"export_config_desc\">Save or share your settings of this module with others.\\n\n        \\\"Save\\\" will prompt to select a location to export the settings as a JSON file.\n    </string>\n    <string name=\"export_complete\">Export complete!</string>\n    <string name=\"share\">Share</string>\n    <string name=\"select_a_location\">Select a location to save.</string>\n    <string name=\"share_config_file\">Share configuration file</string>\n    <string name=\"import_config\">Import configuration</string>\n    <string name=\"import_config_desc\">This will open a system window to select the configuration file.</string>\n    <string name=\"share_error\">Share error!</string>\n    <string name=\"read_error\">Read error! Make sure to select a proper file!</string>\n    <string name=\"import_complete\">Import complete!</string>\n    <string name=\"advanced_options\">Advanced options</string>\n\n    <!--feature_customize.xml-->\n    <string name=\"save\">Save</string>\n\n    <!--advanced_options_activity.xml-->\n    <string name=\"enable_verbose_logging\">Enable verbose logging</string>\n    <string name=\"enable_verbose_logging_desc\">More debug information in LSPosed / EdXposed logs and Android logcat.\\n\n        To share logs, open LSPosed / EdXposed -> Logs -> top Save icon. Then share the created file.\n    </string>\n    <string name=\"spoof_android_version\">Spoof android version.</string>\n    <string name=\"spoof_android_version_desc\">Do not set an Android version greater than your current version.\\n\n        Example, if your Android version is <b>Pie</b>, you can set <b>Nougat / Oreo / Pie</b>, but not Q / R / S.\n    </string>\n    <string name=\"this_might_cause_crash\">This might cause Google Photos to immediately crash!</string>\n    <string name=\"dont_spoof_android_version\">Don\\'t spoof Android version <b>(Recommended)</b></string>\n    <string name=\"follow_spoof_device_version\">Follow spoof device Android version.</string>\n    <string name=\"manually_set_android_version\">Manually set Android version.</string>\n\n    <!--Utils.kt-->\n    <string name=\"killing_please_wait\">Killing… please wait.</string>\n    <string name=\"failed_to_stop_package\">Failed to stop package. Please stop manually.</string>\n    <string name=\"failed_to_launch_package\">Failed to launch. Please start manually.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.PixelifyGooglePhotos\" parent=\"Theme.MaterialComponents.DayNight.DarkActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/purple_500</item>\n        <item name=\"colorPrimaryVariant\">@color/purple_700</item>\n        <item name=\"colorOnPrimary\">@color/white</item>\n        <!-- Secondary brand color. -->\n        <item name=\"colorSecondary\">@color/teal_200</item>\n        <item name=\"colorSecondaryVariant\">@color/teal_700</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <!-- Customize your theme here. -->\n        <item name=\"colorAccent\">@color/purple_500</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.PixelifyGooglePhotos\" parent=\"Theme.MaterialComponents.DayNight.DarkActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/purple_200</item>\n        <item name=\"colorPrimaryVariant\">@color/purple_700</item>\n        <item name=\"colorOnPrimary\">@color/black</item>\n        <!-- Secondary brand color. -->\n        <item name=\"colorSecondary\">@color/teal_200</item>\n        <item name=\"colorSecondaryVariant\">@color/teal_200</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <!-- Customize your theme here. -->\n        <item name=\"colorAccent\">@color/purple_200</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<resources>\n\n    <!--Things to update in new versions:\n\n    build.gradle: versionCode\n    build.gradle: versionName\n\n    update_info.json\n\n    changelog\n    -->\n\n    <string name=\"app_name\">Pixelify Google Photos</string>\n\n    <!--activity_main.xml-->\n    <string name=\"reset\">還原</string>\n    <string name=\"device_to_spoof\">裝置模擬</string>\n    <string name=\"spoofs_build_and_features\">修改 build.prop 下的裝置參數</string>\n    <string name=\"customize_feature_flags\">自定義功能參數</string>\n    <string name=\"feature_flags_changed\">功能參數已更改！</string>\n    <string name=\"spoof_only_in_google_photos\">確保僅對 Google 相簿進行 HOOK</string>\n    <string name=\"spoof_only_in_google_photos_desc\">因 LSPosed 中僅使用白名單，預設該模組僅針對 Google 相簿啟用，\n     如果您想在其他應用中使用它，請保持此開關 <b>關閉</b>.</string>\n    <string name=\"override_rom_feature_levels\">覆蓋 ROM 自訂功能</string>\n    <string name=\"override_rom_feature_levels_desc\">如果使用者選擇較低版本的 Pixel，\n    將避免部分 ROM 內含有模擬 Pixel 的功能影響 Pixelify Google Photos <b>(推薦啟用)</b></string>\n    <string name=\"force_stop_google_photos\">強制停止\\nGoogle 相簿</string>\n    <string name=\"module_not_enabled\">\n        \\n\n        此模組似乎未啟用。\\n\n        請在 LSPosed / EdXposed 中啟用它，然後重新啟動。\\n\n        如果已啟用，可能需強制強制停止 Pixelify Google Photos 後重啟。\\n\n        如果沒用，請移除並重新安裝。\n    </string>\n    <string name=\"close\">關閉</string>\n    <string name=\"open_google_photos\">開啟 Google 相簿</string>\n    <string name=\"telegram_group\">Telegram 討論組！</string>\n    <string name=\"update_available\">有最新的更新可用！</string>\n\n    <!--menu_activity_main.xml-->\n    <string name=\"changelog\">更改日誌</string>\n\n    <!--ActivityMain.xml-->\n    <string name=\"please_force_stop_google_photos\">請強制停止 Google 相簿以使其生效。</string>\n    <string name=\"export_config\">匯出配置</string>\n    <string name=\"export_config_desc\">備份或與他人分享此模組的設定。\\n\n        點選 \\\"儲存\\\" 後將提示選擇一個位置以將設定匯出為 JSON 檔案。\n    </string>\n    <string name=\"export_complete\">匯出成功!</string>\n    <string name=\"share\">分享</string>\n    <string name=\"select_a_location\">選擇要儲存的位置</string>\n    <string name=\"share_config_file\">共享設定檔</string>\n    <string name=\"import_config\">匯入設定檔</string>\n    <string name=\"import_config_desc\">點選後請選擇要匯入的設定檔</string>\n    <string name=\"share_error\">分享錯誤紀錄!</string>\n    <string name=\"read_error\">讀取錯誤! 請確認是否有選擇適合的檔案!</string>\n    <string name=\"import_complete\">匯入成功!</string>\n    <string name=\"advanced_options\">進階設定</string>\n\n    <!--feature_customize.xml-->\n    <string name=\"save\">儲存</string>\n\n    <!--advanced_options_activity.xml-->\n    <string name=\"enable_verbose_logging\">開啟詳細日誌記錄</string>\n    <string name=\"enable_verbose_logging_desc\">LSPosed / EdXposed 日誌和 Android logcat 中的的除錯資訊。\\n\n        如果要匯出日誌，請開啟 LSPosed / EdXposed -> 日誌 -> 頂部儲存圖示。然後分享儲存後的檔案或壓縮檔。\n    </string>\n    <string name=\"spoof_android_version\">模擬 Android 版本</string>\n    <string name=\"spoof_android_version_desc\">不要設定高於您當前版本 Android 的版本。\\n\n        例如，如果您的 Android 版本是 <b>Pie</b>，那您可以設定<b>Nougat / Oreo / Pie</b>，而不能設定以下的版本 Q / R / S.\n    </string>\n    <string name=\"this_might_cause_crash\">這可能會導致 Google 相簿立即崩潰！</string>\n    <string name=\"dont_spoof_android_version\">不要模擬 Android 的版本 <b>(推薦)</b></string>\n    <string name=\"follow_spoof_device_version\">與選擇模擬的裝置版本一致</string>\n    <string name=\"manually_set_android_version\">手動設定 Android 的版本</string>\n\n    <!--Utils.kt-->\n    <string name=\"killing_please_wait\">強制停止中…請稍候。</string>\n    <string name=\"failed_to_stop_package\">無法自動強制停止。請手動強制停止。</string>\n    <string name=\"failed_to_launch_package\">無法自動啟動。請手動啟動。</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/provider_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--Guide at: https://infinum.com/the-capsized-eight/share-files-using-fileprovider-->\n<paths>\n    <cache-path name=\"cache\" path=\"/\" />\n</paths>"
  },
  {
    "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:7.1.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10\"\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 Dec 01 22:54:51 IST 2021\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.2-bin.zip\ndistributionPath=wrapper/dists\nzipStorePath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\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\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official"
  },
  {
    "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        jcenter() // Warning: this repository is going to shut down soon\n    }\n}\nrootProject.name = \"Pixelify Google Photos\"\ninclude ':app'\n"
  },
  {
    "path": "update_info.json",
    "content": "{\n\t\"latest_version_code\": 5\n}\n"
  }
]