diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java b/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
index 490091000..5f30fa285 100644
--- a/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
+++ b/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
@@ -36,7 +36,7 @@ public final class APKInstall {
// @WorkerThread
public static void install(Context context, File apk) {
try (var src = new FileInputStream(apk);
- var out = openStream(context, true)) {
+ var out = openStream(context)) {
if (out != null)
transfer(src, out);
} catch (IOException e) {
@@ -44,7 +44,7 @@ public final class APKInstall {
}
}
- public static OutputStream openStream(Context context, boolean silent) {
+ public static OutputStream openStream(Context context) {
//noinspection InlinedApi
var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
var intent = new Intent(ACTION_SESSION_UPDATE).setPackage(context.getPackageName());
@@ -52,7 +52,7 @@ public final class APKInstall {
var installer = context.getPackageManager().getPackageInstaller();
var params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
- if (silent && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
params.setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED);
}
try {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index be547f11a..0899e6ccd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -46,6 +46,7 @@
+
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Config.kt b/app/src/main/java/com/topjohnwu/magisk/core/Config.kt
index e5b283002..af7cceb3e 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/Config.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/Config.kt
@@ -61,6 +61,7 @@ object Config : PreferenceModel, DBConfig {
const val BOOT_ID = "boot_id"
const val ASKED_HOME = "asked_home"
const val DOH = "doh"
+ const val SHOW_UPDATE_DONE = "update_done"
}
object Value {
@@ -133,6 +134,7 @@ object Config : PreferenceModel, DBConfig {
var suTapjack by preference(Key.SU_TAPJACK, true)
var checkUpdate by preference(Key.CHECK_UPDATES, true)
var doh by preference(Key.DOH, false)
+ var showUpdateDone by preference(Key.SHOW_UPDATE_DONE, false)
var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false)
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Const.kt b/app/src/main/java/com/topjohnwu/magisk/core/Const.kt
index 984a46a7b..c6408d3e5 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/Const.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/Const.kt
@@ -37,8 +37,6 @@ object Const {
object ID {
const val JOB_SERVICE_ID = 7
- const val UPDATE_NOTIFICATION_CHANNEL = "update"
- const val PROGRESS_NOTIFICATION_CHANNEL = "progress"
}
object Url {
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/JobService.kt b/app/src/main/java/com/topjohnwu/magisk/core/JobService.kt
index 29734db77..702504fee 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/JobService.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/JobService.kt
@@ -23,16 +23,20 @@ class JobService : BaseJobService() {
override fun onStartJob(params: JobParameters): Boolean {
val coroutineScope = CoroutineScope(Dispatchers.IO + job)
coroutineScope.launch {
- svc.fetchUpdate()?.run {
- Info.remote = this
- if (Info.env.isActive && BuildConfig.VERSION_CODE < magisk.versionCode)
- Notifications.managerUpdate(this@JobService)
- }
+ doWork()
jobFinished(params, false)
}
return false
}
+ private suspend fun doWork() {
+ svc.fetchUpdate()?.let {
+ Info.remote = it
+ if (Info.env.isActive && BuildConfig.VERSION_CODE < it.magisk.versionCode)
+ Notifications.updateAvailable(this)
+ }
+ }
+
override fun onStopJob(params: JobParameters): Boolean {
job.cancel()
return false
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Receiver.kt b/app/src/main/java/com/topjohnwu/magisk/core/Receiver.kt
index d500f91b8..fa986db96 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/Receiver.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/Receiver.kt
@@ -5,6 +5,7 @@ import android.content.ContextWrapper
import android.content.Intent
import com.topjohnwu.magisk.core.base.BaseReceiver
import com.topjohnwu.magisk.di.ServiceLocator
+import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.GlobalScope
@@ -45,6 +46,12 @@ open class Receiver : BaseReceiver() {
getPkg(intent)?.let { Shell.su("magisk --denylist rm $it").submit() }
}
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setupDynamic(context)
+ Intent.ACTION_MY_PACKAGE_REPLACED -> {
+ if (Config.showUpdateDone) {
+ Notifications.updateDone(context)
+ Config.showUpdateDone = false
+ }
+ }
}
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
index f57778234..091023bbb 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
@@ -11,10 +11,7 @@ import androidx.core.net.toFile
import androidx.lifecycle.LifecycleOwner
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.StubApk
-import com.topjohnwu.magisk.core.ActivityTracker
-import com.topjohnwu.magisk.core.Info
-import com.topjohnwu.magisk.core.intent
-import com.topjohnwu.magisk.core.isRunningAsStub
+import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.ktx.copyAndClose
@@ -75,32 +72,49 @@ class DownloadService : NotificationService() {
}
}
+ private fun openApkSession(): OutputStream {
+ Config.showUpdateDone = true
+ return APKInstall.openStream(this)
+ }
+
private suspend fun handleApp(stream: InputStream, subject: Subject.App) {
- fun write(output: OutputStream) {
+ fun writeTee(output: OutputStream) {
val external = subject.externalFile.outputStream()
stream.copyAndClose(TeeOutputStream(external, output))
}
if (isRunningAsStub) {
- val apk = subject.file.toFile()
- val id = subject.notifyId
+ val updateApk = StubApk.update(this)
try {
- write(StubApk.update(this).outputStream())
+ // Download full APK to stub update path
+ writeTee(updateApk.outputStream())
+
if (Info.stub!!.version < subject.stub.versionCode) {
// Also upgrade stub
- update(id) {
+ update(subject.notifyId) {
it.setProgress(0, 0, true)
.setContentTitle(getString(R.string.hide_app_title))
.setContentText("")
}
+
+ // Download
+ val apk = subject.file.toFile()
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
+
+ // Patch
val patched = File(apk.parent, "patched.apk")
val label = applicationInfo.nonLocalizedLabel
if (!HideAPK.patch(this, apk, patched, packageName, label)) {
throw IOException("HideAPK patch error")
}
apk.delete()
- patched.renameTo(apk)
+
+ // Install
+ val receiver = APKInstall.register(this, null, null)
+ patched.inputStream().copyAndClose(openApkSession())
+ subject.intent = receiver.waitIntent()
+
+ patched.delete()
} else {
ActivityTracker.foreground?.let {
// Relaunch the process if we are foreground
@@ -112,12 +126,15 @@ class DownloadService : NotificationService() {
return
}
} catch (e: Exception) {
- StubApk.update(this).delete()
+ // If any error occurred, do not let stub load the new APK
+ updateApk.delete()
+ throw e
}
+ } else {
+ val receiver = APKInstall.register(this, null, null)
+ writeTee(openApkSession())
+ subject.intent = receiver.waitIntent()
}
- val receiver = APKInstall.register(this, null, null)
- write(APKInstall.openStream(this, false))
- subject.intent = receiver.waitIntent()
}
private fun handleModule(src: InputStream, file: Uri) {
diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
index 98b93745b..b2e9aa90b 100644
--- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
@@ -15,7 +15,10 @@ import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseMainActivity
import com.topjohnwu.magisk.arch.BaseViewModel
-import com.topjohnwu.magisk.core.*
+import com.topjohnwu.magisk.core.Config
+import com.topjohnwu.magisk.core.Const
+import com.topjohnwu.magisk.core.Info
+import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
import com.topjohnwu.magisk.di.viewModel
import com.topjohnwu.magisk.ktx.startAnimations
@@ -48,10 +51,11 @@ class MainActivity : BaseMainActivity() {
setContentView()
showUnsupportedMessage()
askForHomeShortcut()
+ Config.showUpdateDone = false
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
- navigation?.addOnDestinationChangedListener { _, destination, _ ->
+ navigation.addOnDestinationChangedListener { _, destination, _ ->
isRootFragment = when (destination.id) {
R.id.homeFragment,
R.id.modulesFragment,
diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt
index 59179b449..b253f4bd1 100644
--- a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt
@@ -1,15 +1,16 @@
package com.topjohnwu.magisk.view
+import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.PendingIntent
import android.content.Context
+import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toIcon
import com.topjohnwu.magisk.R
-import com.topjohnwu.magisk.core.Const.ID.PROGRESS_NOTIFICATION_CHANNEL
-import com.topjohnwu.magisk.core.Const.ID.UPDATE_NOTIFICATION_CHANNEL
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.di.AppContext
@@ -21,51 +22,75 @@ object Notifications {
val mgr by lazy { AppContext.getSystemService()!! }
- private const val APK_UPDATE_NOTIFICATION_ID = 5
- private val nextId = AtomicInteger(APK_UPDATE_NOTIFICATION_ID)
+ private const val APP_UPDATED_NOTIFICATION_ID = 4
+ private const val APP_UPDATE_NOTIFICATION_ID = 5
+
+ private const val UPDATE_CHANNEL = "update"
+ private const val PROGRESS_CHANNEL = "progress"
+ private const val UPDATED_CHANNEL = "updated"
+
+ private val nextId = AtomicInteger(APP_UPDATE_NOTIFICATION_ID)
fun setup(context: Context) {
if (SDK_INT >= 26) {
- val channel = NotificationChannel(UPDATE_NOTIFICATION_CHANNEL,
+ val channel = NotificationChannel(UPDATE_CHANNEL,
context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT)
- val channel2 = NotificationChannel(PROGRESS_NOTIFICATION_CHANNEL,
+ val channel2 = NotificationChannel(PROGRESS_CHANNEL,
context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW)
- mgr.createNotificationChannels(listOf(channel, channel2))
+ val channel3 = NotificationChannel(UPDATED_CHANNEL,
+ context.getString(R.string.updated_channel), NotificationManager.IMPORTANCE_HIGH)
+ mgr.createNotificationChannels(listOf(channel, channel2, channel3))
}
}
- private fun updateBuilder(context: Context): Notification.Builder {
- return Notification.Builder(context).apply {
- val bitmap = context.getBitmap(R.drawable.ic_magisk_outline)
- setLargeIcon(bitmap)
- if (SDK_INT >= 26) {
- setSmallIcon(bitmap.toIcon())
- setChannelId(UPDATE_NOTIFICATION_CHANNEL)
- } else {
- setSmallIcon(R.drawable.ic_magisk_outline)
- }
+ @SuppressLint("InlinedApi")
+ fun updateDone(context: Context) {
+ val pm = context.packageManager
+ val intent = pm.getLaunchIntentForPackage(context.packageName) ?: return
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ val pending = PendingIntent.getActivity(context, 0, intent, flag)
+ val builder = if (SDK_INT >= 26) {
+ Notification.Builder(context, UPDATED_CHANNEL)
+ .setSmallIcon(context.getBitmap(R.drawable.ic_magisk_outline).toIcon())
+ } else {
+ Notification.Builder(context).setPriority(Notification.PRIORITY_HIGH)
+ .setSmallIcon(R.drawable.ic_magisk_outline)
}
+ .setContentIntent(pending)
+ .setContentTitle(context.getText(R.string.updated_title))
+ .setContentText(context.getText(R.string.updated_text))
+ .setAutoCancel(true)
+ mgr.notify(APP_UPDATED_NOTIFICATION_ID, builder.build())
}
- fun managerUpdate(context: Context) {
+ fun updateAvailable(context: Context) {
val intent = DownloadService.getPendingIntent(context, Subject.App())
- val builder = updateBuilder(context)
+ val bitmap = context.getBitmap(R.drawable.ic_magisk_outline)
+ val builder = if (SDK_INT >= 26) {
+ Notification.Builder(context, UPDATE_CHANNEL)
+ .setSmallIcon(bitmap.toIcon())
+ } else {
+ Notification.Builder(context)
+ .setSmallIcon(R.drawable.ic_magisk_outline)
+ }
+ .setLargeIcon(bitmap)
.setContentTitle(context.getString(R.string.magisk_update_title))
.setContentText(context.getString(R.string.manager_download_install))
.setAutoCancel(true)
.setContentIntent(intent)
- mgr.notify(APK_UPDATE_NOTIFICATION_ID, builder.build())
+ mgr.notify(APP_UPDATE_NOTIFICATION_ID, builder.build())
}
fun progress(context: Context, title: CharSequence): Notification.Builder {
val builder = if (SDK_INT >= 26) {
- Notification.Builder(context, PROGRESS_NOTIFICATION_CHANNEL)
+ Notification.Builder(context, PROGRESS_CHANNEL)
} else {
Notification.Builder(context).setPriority(Notification.PRIORITY_LOW)
}
- builder.setSmallIcon(android.R.drawable.stat_sys_download)
+ .setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(title)
.setProgress(0, 0, true)
.setOngoing(true)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c85a0185a..afe34bd19 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -190,9 +190,12 @@
Magisk Updates
Progress Notifications
+ Update Complete
Download complete
Error downloading file
Magisk Update Available!
+ Magisk Updated
+ Tap to open app
Yes