-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b5238f5
Showing
37 changed files
with
1,516 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea/caches | ||
/.idea/libraries | ||
/.idea/modules.xml | ||
/.idea/workspace.xml | ||
/.idea/navEditor.xml | ||
/.idea/assetWizardSettings.xml | ||
*.apk | ||
.DS_Store | ||
/build | ||
/captures | ||
.externalNativeBuild | ||
.cxx | ||
.idea | ||
/app/build | ||
local.properties | ||
/release-stuff |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Let Me Downgrade | ||
<img align="left" src="play-store-images/ic_launcher-playstore.png" width="140" /> | ||
|
||
Android's app installation system does not allow users to downgrade to an older version of an app when they already have a newer version installed. On Android 5 through 12, the [XDowngrader](https://github.com/Xposed-Modules-Repo/com.alex193a.xdowngrader) Xposed module could be used on rooted devices to bypass this limitation. As of Android 13, however, XDowngrader no longer works. Let Me Downgrade is an Xposed module that replicates this functionality for Android 13, and as an added bonus, provides a Quick Settings tile to easily enable and disable the downgrade block. | ||
|
||
**⚠️ WARNING:** Let Me Downgrade is intended for rooted devices running Android 13 and requires Xposed. The recommended Xposed variant to use is LSPosed. Other Xposed variants may work, but have not been tested. Additionally, this module cannot be guaranteed to work on all devices. In the worst case, it can cause a bootloop. Use at your own risk. | ||
|
||
<p align="center"> | ||
<a href="https://play.google.com/store/apps/details?id=com.berdik.letmedowngrade"> | ||
<img src="play-store-images/google-play-badge.png" height="80" /> | ||
</a> | ||
<a href="https://github.com/DavidBerdik/Let-Me-Downgrade/releases"> | ||
<img src="play-store-images/badge_github.png" height="80" /> | ||
</a> | ||
</p> | ||
|
||
To use Let Me Downgrade: | ||
1. Install LSposed. This requires your device to be rooted with Magisk. Installation instructions for LSPosed are available [here](https://github.com/LSPosed/LSPosed#install). | ||
2. Install Let Me Downgrade. | ||
3. Activate the Let Me Downgrade module in the LSposed user interface. | ||
|
||
<p align="center"> | ||
<img src="play-store-images/screenshots/1.png" width="300" /> | ||
<img src="play-store-images/screenshots/2.png" width="300" /> | ||
</p> | ||
|
||
4. Reboot your device and sign in. | ||
|
||
<p align="center"> | ||
<img src="play-store-images/screenshots/3.png" width="300" /> | ||
</p> | ||
|
||
5. Open the quick settings panel. The Let Me Downgrade tile will appear. | ||
|
||
<p align="center"> | ||
<img src="play-store-images/screenshots/4.png" width="300" /> | ||
</p> | ||
|
||
6. Toggle the Let Me Downgrade tile on or off to enable or disable it. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
plugins { | ||
id 'com.android.application' | ||
id 'kotlin-android' | ||
} | ||
|
||
android { | ||
compileSdk 33 | ||
|
||
defaultConfig { | ||
applicationId "com.berdik.letmedowngrade" | ||
minSdk 33 | ||
targetSdk 33 | ||
versionCode 1 | ||
versionName "1.0.0" | ||
} | ||
|
||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
|
||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_11 | ||
targetCompatibility JavaVersion.VERSION_11 | ||
} | ||
|
||
kotlinOptions { | ||
jvmTarget = '11' | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation 'androidx.appcompat:appcompat:1.5.0' | ||
implementation 'com.github.kyuubiran:EzXHelper:1.0.1' | ||
implementation 'com.github.kyuubiran:EzXHelper:1.0.1:sources' | ||
compileOnly 'de.robv.android.xposed:api:82' | ||
compileOnly 'de.robv.android.xposed:api:82:sources' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.berdik.letmedowngrade"> | ||
|
||
<application | ||
android:label="@string/app_name" | ||
android:icon="@mipmap/ic_launcher"> | ||
<meta-data | ||
android:name="xposedmodule" | ||
android:value="true" /> | ||
<meta-data | ||
android:name="xposeddescription" | ||
android:value="@string/xposed_description" /> | ||
<meta-data | ||
android:name="xposedminversion" | ||
android:value="93" /> | ||
<meta-data | ||
android:name="xposedscope" | ||
android:resource="@array/xposed_scope" /> | ||
|
||
<service android:name="com.berdik.letmedowngrade.QuickTile" | ||
android:icon="@drawable/ic_baseline_arrow_downward_24" | ||
android:label="@string/tile_label" | ||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" | ||
android:exported="true"> | ||
<intent-filter> | ||
<action android:name="android.service.quicksettings.action.QS_TILE" /> | ||
</intent-filter> | ||
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" | ||
android:value="true" /> | ||
</service> | ||
</application> | ||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.berdik.letmedowngrade.LetMeDowngrade |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions
48
app/src/main/java/com/berdik/letmedowngrade/LetMeDowngrade.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.berdik.letmedowngrade | ||
|
||
import com.berdik.letmedowngrade.hookers.SystemUIHooker | ||
import com.berdik.letmedowngrade.hookers.PackageManagerServiceHooker | ||
import com.github.kyuubiran.ezxhelper.init.EzXHelperInit | ||
import de.robv.android.xposed.IXposedHookLoadPackage | ||
import de.robv.android.xposed.IXposedHookZygoteInit | ||
import de.robv.android.xposed.XSharedPreferences | ||
import de.robv.android.xposed.XposedBridge | ||
import de.robv.android.xposed.callbacks.XC_LoadPackage | ||
|
||
class LetMeDowngrade : IXposedHookZygoteInit, IXposedHookLoadPackage { | ||
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) { | ||
EzXHelperInit.initZygote(startupParam) | ||
} | ||
|
||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { | ||
if (lpparam != null) { | ||
EzXHelperInit.initHandleLoadPackage(lpparam) | ||
EzXHelperInit.setLogTag("Let Me Downgrade") | ||
EzXHelperInit.setToastTag("Let Me Downgrade") | ||
|
||
when (lpparam.packageName) { | ||
"android" -> { | ||
try { | ||
PackageManagerServiceHooker.hook(lpparam) | ||
} catch (e: Exception) { | ||
XposedBridge.log("[Let Me Downgrade] ERROR: $e") | ||
} | ||
} | ||
} | ||
|
||
when (lpparam.packageName) { | ||
"com.android.systemui" -> { | ||
val prefs = XSharedPreferences(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID) | ||
if (!prefs.getBoolean("tileRevealDone", false)) { | ||
try { | ||
XposedBridge.log("[Let Me Downgrade] Hooking System UI to add and reveal quick settings tile.") | ||
SystemUIHooker.hook(lpparam) | ||
} catch (e: Exception) { | ||
XposedBridge.log("[Let Me Downgrade] ERROR: $e") | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
app/src/main/java/com/berdik/letmedowngrade/PrefManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.berdik.letmedowngrade | ||
|
||
import android.annotation.SuppressLint | ||
import android.content.Context | ||
import android.content.SharedPreferences | ||
|
||
class PrefManager { | ||
companion object { | ||
private var prefs: SharedPreferences? = null | ||
private var hookActive: Boolean? = null | ||
|
||
// Since we are an Xposed module, we don't care about MODE_WORLD_READABLE being deprecated. | ||
// In fact, we need to use it despite being deprecated because without it, the Xposed | ||
// hooking mechanism cannot access the preference value. | ||
@SuppressLint("WorldReadableFiles") | ||
@Suppress("DEPRECATION") | ||
fun loadPrefs(context: Context) { | ||
if (prefs == null) { | ||
prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_WORLD_READABLE) | ||
markTileRevealAsDone() | ||
} | ||
} | ||
|
||
fun isHookOn(): Boolean { | ||
if (hookActive == null) { | ||
hookActive = prefs!!.getBoolean("hookActive", false) | ||
} | ||
return hookActive as Boolean | ||
} | ||
|
||
fun toggleHookState() { | ||
hookActive = !isHookOn() | ||
val prefEdit = prefs!!.edit() | ||
prefEdit.putBoolean("hookActive", hookActive!!) | ||
prefEdit.apply() | ||
} | ||
|
||
private fun markTileRevealAsDone() { | ||
val prefEdit = prefs!!.edit() | ||
prefEdit.putBoolean("tileRevealDone", true) | ||
prefEdit.apply() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.berdik.letmedowngrade | ||
|
||
import android.service.quicksettings.Tile | ||
import android.service.quicksettings.TileService | ||
|
||
class QuickTile: TileService() { | ||
override fun onStartListening() { | ||
super.onStartListening() | ||
PrefManager.loadPrefs(applicationContext) | ||
setButtonState() | ||
} | ||
|
||
override fun onClick() { | ||
super.onClick() | ||
PrefManager.toggleHookState() | ||
setButtonState() | ||
} | ||
|
||
private fun setButtonState() { | ||
if (PrefManager.isHookOn()) | ||
qsTile.state = Tile.STATE_ACTIVE | ||
else | ||
qsTile.state = Tile.STATE_INACTIVE | ||
qsTile.updateTile() | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
app/src/main/java/com/berdik/letmedowngrade/hookers/PackageManagerServiceHooker.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.berdik.letmedowngrade.hookers | ||
|
||
import android.annotation.SuppressLint | ||
import com.berdik.letmedowngrade.BuildConfig | ||
import com.github.kyuubiran.ezxhelper.utils.* | ||
import de.robv.android.xposed.XSharedPreferences | ||
import de.robv.android.xposed.XposedBridge | ||
import de.robv.android.xposed.XposedHelpers | ||
import de.robv.android.xposed.callbacks.XC_LoadPackage | ||
|
||
class PackageManagerServiceHooker { | ||
companion object { | ||
@SuppressLint("PrivateApi") | ||
fun hook(lpparam: XC_LoadPackage.LoadPackageParam) { | ||
findAllMethods(lpparam.classLoader.loadClass("com.android.server.pm.PackageManagerServiceUtils")) { | ||
name == "checkDowngrade" | ||
}.hookMethod { | ||
var packageName = "" | ||
var isHookActive = false | ||
|
||
before { param -> | ||
// Get the package name of the app being processed. | ||
packageName = XposedHelpers.getObjectField(param.args[1], "packageName") as String | ||
|
||
// Get the active state of the hook. | ||
val prefs = XSharedPreferences(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID) | ||
isHookActive = prefs.getBoolean("hookActive", false) | ||
|
||
// If the hook is active, log a block of the downgrade check and bypass the real function. | ||
if (isHookActive) { | ||
XposedBridge.log("[Let Me Downgrade] Blocked downgrade check on package: $packageName") | ||
param.result = null | ||
} | ||
} | ||
|
||
after { | ||
// If the hook is not active, make a log entry after the real function executes indicating so. | ||
if (!isHookActive) { | ||
XposedBridge.log("[Let Me Downgrade] Allowed downgrade check on package: $packageName") | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.