Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve installation robustness #1528

Merged
merged 24 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5556a0d
feat: Improve installation robustness
TheAabedKhan Nov 27, 2023
55cf84a
Merge branch 'dev' into implement-packageinstaller-api
TheAabedKhan Dec 2, 2023
aae748e
refactor: apply changes from suggestions
TheAabedKhan Dec 2, 2023
7fdd536
Merge branch 'dev' into implement-packageinstaller-api
TheAabedKhan Dec 2, 2023
0b853c2
Update assets/i18n/en_US.json [skip ci]
oSumAtrIX Dec 2, 2023
36d2e48
feat: append patch version to the APK name while sharing/exporting (#…
dhruvanbhalara Dec 5, 2023
6a8d10e
Merge branch 'dev' into implement-packageinstaller-api
TheAabedKhan Dec 5, 2023
9e003ed
refactor: code formatting
TheAabedKhan Dec 5, 2023
c4bf940
Merge branch 'dev' into implement-packageinstaller-api
TheAabedKhan Dec 5, 2023
e03a67b
refactor: code formatting
TheAabedKhan Dec 5, 2023
fdb3a1d
refactor: default string
TheAabedKhan Dec 5, 2023
c388aab
refactor: modify strings
TheAabedKhan Dec 5, 2023
2e69aa5
refactor: apply changes from suggestion
TheAabedKhan Dec 5, 2023
be1a5da
Update assets/i18n/en_US.json
TheAabedKhan Dec 6, 2023
a812a58
refactor: apply changes from suggestion
TheAabedKhan Dec 6, 2023
63ddc1c
Update assets/i18n/en_US.json
TheAabedKhan Dec 7, 2023
11e3e89
Update lib/services/root_api.dart
TheAabedKhan Dec 9, 2023
0819376
feat: more string consistency
Ushie Dec 9, 2023
1494f17
Merge branch 'implement-packageinstaller-api' of https://github.com/t…
Ushie Dec 9, 2023
d7f9d6f
Merge branch 'dev' into implement-packageinstaller-api
validcube Dec 11, 2023
68db4a1
Merge branch 'dev' into implement-packageinstaller-api
validcube Dec 22, 2023
a9dfb84
Update assets/i18n/en_US.json
oSumAtrIX Dec 22, 2023
abd43da
Update button
validcube Dec 23, 2023
5c39cba
Update style
validcube Dec 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ExportSettingsActivity"
android:exported="true">
</activity>
<activity
android:name=".ExportSettingsActivity"
android:exported="true">
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
Expand All @@ -55,5 +55,22 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

<receiver
android:name=".utils.packageInstaller.InstallerReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="APP_INSTALL_ACTION" />
</intent-filter>
</receiver>
<receiver
android:name=".utils.packageInstaller.UninstallerReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="APP_UNINSTALL_ACTION" />
</intent-filter>
</receiver>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package app.revanced.manager.flutter

import android.app.PendingIntent
import android.app.SearchManager
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.Build
import android.os.Handler
import android.os.Looper
import app.revanced.manager.flutter.utils.Aapt
import app.revanced.manager.flutter.utils.aligning.ZipAligner
import app.revanced.manager.flutter.utils.packageInstaller.InstallerReceiver
import app.revanced.manager.flutter.utils.packageInstaller.UninstallerReceiver
import app.revanced.manager.flutter.utils.signing.Signer
import app.revanced.manager.flutter.utils.zip.ZipFile
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
Expand Down Expand Up @@ -184,12 +189,24 @@ class MainActivity : FlutterActivity() {
}.toString().let(result::success)
}

"installApk" -> {
val apkPath = call.argument<String>("apkPath")!!
PackageInstallerManager.result = result
installApk(apkPath)
}

"uninstallApp" -> {
val packageName = call.argument<String>("packageName")!!
uninstallApp(packageName)
PackageInstallerManager.result = result
}

else -> result.notImplemented()
}
}
}

fun openBrowser(query: String?) {
private fun openBrowser(query: String?) {
val intent = Intent(Intent.ACTION_WEB_SEARCH).apply {
putExtra(SearchManager.QUERY, query)
}
Expand Down Expand Up @@ -407,4 +424,44 @@ class MainActivity : FlutterActivity() {
handler.post { result.success(null) }
}.start()
}

private fun installApk(apkPath: String) {
val packageInstaller: PackageInstaller = applicationContext.packageManager.packageInstaller
val sessionParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId: Int = packageInstaller.createSession(sessionParams)
val session: PackageInstaller.Session = packageInstaller.openSession(sessionId)
session.use { activeSession ->
val sessionOutputStream = activeSession.openWrite(applicationContext.packageName, 0, -1)
sessionOutputStream.use { outputStream ->
val apkFile = File(apkPath)
apkFile.inputStream().use { inputStream ->
inputStream.copyTo(outputStream)
}
}
}
val receiverIntent = Intent(applicationContext, InstallerReceiver::class.java).apply {
action = "APP_INSTALL_ACTION"
}
val receiverPendingIntent = PendingIntent.getBroadcast(context, sessionId, receiverIntent, PackageInstallerManager.flags)
session.commit(receiverPendingIntent.intentSender)
session.close()
}

private fun uninstallApp(packageName: String) {
val packageInstaller: PackageInstaller = applicationContext.packageManager.packageInstaller
val receiverIntent = Intent(applicationContext, UninstallerReceiver::class.java).apply {
action = "APP_UNINSTALL_ACTION"
}
val receiverPendingIntent = PendingIntent.getBroadcast(context, 0, receiverIntent, PackageInstallerManager.flags)
packageInstaller.uninstall(packageName, receiverPendingIntent.intentSender)
}

object PackageInstallerManager {
var result: MethodChannel.Result? = null
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package app.revanced.manager.flutter.utils.packageInstaller

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import app.revanced.manager.flutter.MainActivity

class InstallerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
if (confirmationIntent != null) {
context.startActivity(confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
}

else -> {
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
val otherPackageName = intent.getStringExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME)
MainActivity.PackageInstallerManager.result!!.success(mapOf(
"status" to status,
"packageName" to packageName,
"message" to message,
"otherPackageName" to otherPackageName
))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package app.revanced.manager.flutter.utils.packageInstaller

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import app.revanced.manager.flutter.MainActivity

class UninstallerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
if (confirmationIntent != null) {
context.startActivity(confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
}

else -> {
MainActivity.PackageInstallerManager.result!!.success(status)
}
}
}
}
48 changes: 46 additions & 2 deletions assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,15 @@
},
"installerView": {
"widgetTitle": "Installer",
"installType": "Select install type",
"installTypeDescription": "Select the installation type to proceed with.",
"installType": "Choose installation method",
"installTypeDescription": "To install, you can either mount or install normally. Mounting will install the patched app on top of an existing installation.",
TheAabedKhan marked this conversation as resolved.
Show resolved Hide resolved

"installButton": "Install",
"installRootType": "Mount",
"installNonRootType": "Normal",

"warning": "Disable auto updates after installing the app to avoid unexpected issues.",
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved

"pressBackAgain": "Press back again to cancel",
"openButton": "Open",
"shareButton": "Share file",
Expand Down Expand Up @@ -327,5 +329,47 @@
"integrationsContributors": "Integrations contributors",
"cliContributors": "CLI contributors",
"managerContributors": "Manager contributors"
},
"installErrorDialog": {
"title0": "Version mismatch",
"title1": "No installation found",
"title2": "Installation blocked",
"title3_0": "Installation blocked",
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved

"title4": "Installation invalid",
"title4_0": "Can't downgrade",

"title5": "Installation conflict",
"title6": "Installation storage issue",
"title7": "Installation incompatible",
"title8": "Installation timeout",
"title10": "No root access",

"callback0": "To install by mounting the patched app, the version of the installed app must match the version of the patched app.",
"callback1": "To install by mounting the patched app, the unpatched app must be installed on this device.",
"callback2": "The installation was blocked by {packageName}.",
"callback3_0": "The installation was blocked.",

"callback4": "The app is invalid.",
"callback4_0": "The version of the installed app is newer than the version of the patched app.",

"callback5": "An existing installation of the app prevents the installation.",
"callback6": "The app could not be installed due to insufficient storage.",
"callback7": "The app is incompatible with this device.",
"callback8": "The installation took too long.",
"callback10": "To install by mounting the patched app, root access is required.",

"solution0": "Install the version of the app you are mounting and try again.",
"solution2": "Adjust your security settings and try again.",
"solution3_0": "Adjust your security settings and try again.",
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved

"solution4": "Reinstall by uninstalling the app and try again?",
"solution4_0": "Uninstall the current version and try again?",

"solution5": "Uninstall the app and try again?",
TheAabedKhan marked this conversation as resolved.
Show resolved Hide resolved
"solution6": "Free up some space and try again.",
"solution7": "Contact the developer of the app and ask for support.",
"solution8": "Try again.",
TheAabedKhan marked this conversation as resolved.
Show resolved Hide resolved
"solution10": "Grant root access to ReVanced Manager and try again."
}
}
8 changes: 8 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:revanced_manager/services/download_manager.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart';
import 'package:revanced_manager/ui/views/navigation/navigation_view.dart';
import 'package:shared_preferences/shared_preferences.dart';
Expand All @@ -24,6 +25,13 @@ Future main() async {
final String repoUrl = locator<ManagerAPI>().getRepoUrl();
locator<GithubAPI>().initialize(repoUrl);
tz.initializeTimeZones();

// TODO(aAbed): remove in the future, keep it for now during migration.
final rootAPI = RootAPI();
if (await rootAPI.hasRootPermissions()) {
await rootAPI.removeOrphanedFiles();
}

prefs = await SharedPreferences.getInstance();

runApp(const MyApp());
Expand Down
1 change: 0 additions & 1 deletion lib/services/download_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,3 @@ class DownloadManager {
);
}
}

Loading