Update app to target API 33

Close #6206
This commit is contained in:
topjohnwu 2022-08-23 03:59:09 -07:00
parent 3f7f6e619a
commit 928a16d8cc
20 changed files with 114 additions and 64 deletions

View File

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" /> <uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" /> <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" android:maxSdkVersion="29"

View File

@ -39,6 +39,16 @@ public final class APKInstall {
} }
} }
public static void registerReceiver(
Context context, BroadcastReceiver receiver, IntentFilter filter) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// noinspection InlinedApi
context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED);
} else {
context.registerReceiver(receiver, filter);
}
}
public static Session startSession(Context context) { public static Session startSession(Context context) {
return startSession(context, null, null, null); return startSession(context, null, null, null);
} }
@ -51,9 +61,9 @@ public final class APKInstall {
// If pkg is not null, look for package added event // If pkg is not null, look for package added event
var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package"); filter.addDataScheme("package");
context.registerReceiver(receiver, filter); registerReceiver(context, receiver, filter);
} }
context.registerReceiver(receiver, new IntentFilter(receiver.sessionId)); registerReceiver(context, receiver, new IntentFilter(receiver.sessionId));
return receiver; return receiver;
} }

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.arch package com.topjohnwu.magisk.arch
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES import android.Manifest.permission.*
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.databinding.PropertyChangeRegistry import androidx.databinding.PropertyChangeRegistry
@ -53,6 +52,17 @@ abstract class BaseViewModel : ViewModel(), ObservableHost {
} }
} }
@SuppressLint("InlinedApi")
inline fun withPostNotificationPermission(crossinline callback: () -> Unit) {
withPermission(POST_NOTIFICATIONS) {
if (!it) {
SnackbarEvent(R.string.post_notifications_denied).publish()
} else {
callback()
}
}
}
fun back() = BackPressEvent().publish() fun back() = BackPressEvent().publish()
fun <Event : ViewEvent> Event.publish() { fun <Event : ViewEvent> Event.publish() {

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.core.base package com.topjohnwu.magisk.core.base
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES import android.Manifest.permission.*
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
@ -82,12 +81,18 @@ abstract class BaseActivity : AppCompatActivity() {
} }
fun withPermission(permission: String, callback: (Boolean) -> Unit) { fun withPermission(permission: String, callback: (Boolean) -> Unit) {
if (permission == WRITE_EXTERNAL_STORAGE && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { permission == WRITE_EXTERNAL_STORAGE) {
// We do not need external rw on R+ // We do not need external rw on R+
callback(true) callback(true)
return return
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU &&
permission == POST_NOTIFICATIONS) {
// All apps have notification permissions before T
callback(true)
return
}
permissionCallback = callback permissionCallback = callback
if (permission == REQUEST_INSTALL_PACKAGES) { if (permission == REQUEST_INSTALL_PACKAGES) {
requestInstall.launch(Unit) requestInstall.launch(Unit)

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.core.download package com.topjohnwu.magisk.core.download
import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.PendingIntent import android.app.PendingIntent
import android.app.PendingIntent.* import android.app.PendingIntent.*
@ -13,17 +14,14 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.ActivityTracker import com.topjohnwu.magisk.core.ActivityTracker
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.intent import com.topjohnwu.magisk.core.intent
import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.tasks.HideAPK import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.ktx.copyAndClose import com.topjohnwu.magisk.ktx.*
import com.topjohnwu.magisk.ktx.forEach
import com.topjohnwu.magisk.ktx.withStreams
import com.topjohnwu.magisk.ktx.writeTo
import com.topjohnwu.magisk.utils.APKInstall import com.topjohnwu.magisk.utils.APKInstall
import com.topjohnwu.magisk.view.Notifications
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -51,7 +49,7 @@ class DownloadService : NotificationService() {
} }
private fun download(subject: Subject) { private fun download(subject: Subject) {
update(subject.notifyId) notifyUpdate(subject.notifyId)
val coroutineScope = CoroutineScope(job + Dispatchers.IO) val coroutineScope = CoroutineScope(job + Dispatchers.IO)
coroutineScope.launch { coroutineScope.launch {
try { try {
@ -62,7 +60,7 @@ class DownloadService : NotificationService() {
} }
val activity = ActivityTracker.foreground val activity = ActivityTracker.foreground
if (activity != null && subject.autoLaunch) { if (activity != null && subject.autoLaunch) {
remove(subject.notifyId) notifyRemove(subject.notifyId)
subject.pendingIntent(activity)?.send() subject.pendingIntent(activity)?.send()
} else { } else {
notifyFinish(subject) notifyFinish(subject)
@ -92,7 +90,7 @@ class DownloadService : NotificationService() {
if (Info.stub!!.version < subject.stub.versionCode) { if (Info.stub!!.version < subject.stub.versionCode) {
// Also upgrade stub // Also upgrade stub
update(subject.notifyId) { notifyUpdate(subject.notifyId) {
it.setProgress(0, 0, true) it.setProgress(0, 0, true)
.setContentTitle(getString(R.string.hide_app_title)) .setContentTitle(getString(R.string.hide_app_title))
.setContentText("") .setContentText("")
@ -118,7 +116,7 @@ class DownloadService : NotificationService() {
StubApk.restartProcess(it) StubApk.restartProcess(it)
} ?: run { } ?: run {
// Or else kill the current process after posting notification // Or else kill the current process after posting notification
subject.intent = Notifications.selfLaunchIntent(this) subject.intent = selfLaunchIntent()
subject.postDownload = { Runtime.getRuntime().exit(0) } subject.postDownload = { Runtime.getRuntime().exit(0) }
} }
return return
@ -206,8 +204,11 @@ class DownloadService : NotificationService() {
} }
} }
fun start(context: Context, subject: Subject) { @SuppressLint("InlinedApi")
val app = context.applicationContext fun start(activity: BaseActivity, subject: Subject) {
activity.withPermission(Manifest.permission.POST_NOTIFICATIONS) {
// Always download regardless of notification permission status
val app = activity.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
app.startForegroundService(intent(app, subject)) app.startForegroundService(intent(app, subject))
} else { } else {
@ -215,4 +216,5 @@ class DownloadService : NotificationService() {
} }
} }
} }
}
} }

View File

@ -2,6 +2,7 @@ package com.topjohnwu.magisk.core.download
import android.app.Notification import android.app.Notification
import android.content.Intent import android.content.Intent
import android.os.Build
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseService import com.topjohnwu.magisk.core.base.BaseService
@ -30,11 +31,11 @@ open class NotificationService : BaseService() {
val total = max.toFloat() / 1048576 val total = max.toFloat() / 1048576
val id = subject.notifyId val id = subject.notifyId
update(id) { it.setContentTitle(subject.title) } notifyUpdate(id) { it.setContentTitle(subject.title) }
return ProgressInputStream(byteStream()) { return ProgressInputStream(byteStream()) {
val progress = it.toFloat() / 1048576 val progress = it.toFloat() / 1048576
update(id) { notification -> notifyUpdate(id) { notification ->
if (max > 0) { if (max > 0) {
broadcast(progress / total, subject) broadcast(progress / total, subject)
notification notification
@ -49,7 +50,7 @@ open class NotificationService : BaseService() {
} }
private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int { private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int {
val notification = remove(id)?.also(editor) ?: return -1 val notification = notifyRemove(id)?.also(editor) ?: return -1
val newId = Notifications.nextId() val newId = Notifications.nextId()
Notifications.mgr.notify(newId, notification.build()) Notifications.mgr.notify(newId, notification.build())
return newId return newId
@ -73,28 +74,31 @@ open class NotificationService : BaseService() {
subject.pendingIntent(this)?.let { intent -> it.setContentIntent(intent) } subject.pendingIntent(this)?.let { intent -> it.setContentIntent(intent) }
} }
private fun create() = Notifications.progress(this, "") private fun updateForegroundState() {
private fun updateForeground() {
if (hasNotifications) { if (hasNotifications) {
val (id, notification) = notifications.entries.first() val (id, notification) = notifications.entries.first()
startForeground(id, notification.build()) startForeground(id, notification.build())
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_DETACH)
} else { } else {
@Suppress("DEPRECATION")
stopForeground(false) stopForeground(false)
} }
} }
protected fun update(id: Int, editor: (Notification.Builder) -> Unit = {}) { protected fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit = {}) {
fun create() = Notifications.startProgress(this, "")
val wasEmpty = !hasNotifications val wasEmpty = !hasNotifications
val notification = notifications.getOrPut(id, ::create).also(editor) val notification = notifications.getOrPut(id, ::create).also(editor)
if (wasEmpty) if (wasEmpty)
updateForeground() updateForegroundState()
else else
Notifications.mgr.notify(id, notification.build()) Notifications.mgr.notify(id, notification.build())
} }
protected fun remove(id: Int): Notification.Builder? { protected fun notifyRemove(id: Int): Notification.Builder? {
val n = notifications.remove(id)?.also { updateForeground() } val n = notifications.remove(id)?.also { updateForegroundState() }
Notifications.mgr.cancel(id) Notifications.mgr.cancel(id)
return n return n
} }

View File

@ -10,6 +10,7 @@ import android.content.IntentFilter
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.PowerManager import android.os.PowerManager
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import com.topjohnwu.magisk.ktx.registerRuntimeReceiver
@TargetApi(23) @TargetApi(23)
class MarshmallowNetworkObserver( class MarshmallowNetworkObserver(
@ -21,7 +22,7 @@ class MarshmallowNetworkObserver(
init { init {
val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED) val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
app.registerReceiver(receiver, filter) app.registerRuntimeReceiver(receiver, filter)
} }
override fun stopObserving() { override fun stopObserving() {

View File

@ -8,9 +8,7 @@ import com.topjohnwu.magisk.view.MagiskDialog
abstract class DialogEvent : ViewEvent(), ActivityExecutor { abstract class DialogEvent : ViewEvent(), ActivityExecutor {
override fun invoke(activity: UIActivity<*>) { override fun invoke(activity: UIActivity<*>) {
MagiskDialog(activity) MagiskDialog(activity).apply(this::build).show()
.apply { setOwnerActivity(activity) }
.apply(this::build).show()
} }
abstract fun build(dialog: MagiskDialog) abstract fun build(dialog: MagiskDialog)

View File

@ -29,7 +29,7 @@ class ManagerInstallDialog : MarkDownDialog() {
setCancelable(true) setCancelable(true)
setButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
text = R.string.install text = R.string.install
onClick { DownloadService.start(context, Subject.App()) } onClick { DownloadService.start(activity, Subject.App()) }
} }
setButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = android.R.string.cancel text = android.R.string.cancel

View File

@ -5,7 +5,6 @@ import android.widget.TextView
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -24,7 +23,7 @@ abstract class MarkDownDialog : DialogEvent() {
val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null) val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null)
setView(view) setView(view)
val tv = view.findViewById<TextView>(R.id.md_txt) val tv = view.findViewById<TextView>(R.id.md_txt)
(ownerActivity as BaseActivity).lifecycleScope.launch { activity.lifecycleScope.launch {
try { try {
val text = withContext(Dispatchers.IO) { getMarkdownText() } val text = withContext(Dispatchers.IO) { getMarkdownText() }
ServiceLocator.markwon.setMarkdown(tv, text) ServiceLocator.markwon.setMarkdown(tv, text)

View File

@ -24,7 +24,7 @@ class ModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog() {
fun download(install: Boolean) { fun download(install: Boolean) {
val action = if (install) Action.Flash else Action.Download val action = if (install) Action.Flash else Action.Download
val subject = Subject.Module(item, action) val subject = Subject.Module(item, action)
DownloadService.start(context, subject) DownloadService.start(activity, subject)
} }
val title = context.getString(R.string.repo_install_title, val title = context.getString(R.string.repo_install_title,

View File

@ -2,10 +2,7 @@ package com.topjohnwu.magisk.ktx
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.ComponentName import android.content.*
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -33,6 +30,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.utils.RootUtils import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.utils.APKInstall
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import java.io.File import java.io.File
import kotlin.Array import kotlin.Array
@ -266,3 +264,14 @@ fun PackageManager.getPackageInfo(uid: Int, pid: Int): PackageInfo? {
} }
throw PackageManager.NameNotFoundException() throw PackageManager.NameNotFoundException()
} }
fun Context.registerRuntimeReceiver(receiver: BroadcastReceiver, filter: IntentFilter) {
APKInstall.registerReceiver(this, receiver, filter)
}
fun Context.selfLaunchIntent(): Intent {
val pm = packageManager
val intent = pm.getLaunchIntentForPackage(packageName)!!
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
return intent
}

View File

@ -1,5 +1,7 @@
package com.topjohnwu.magisk.ui package com.topjohnwu.magisk.ui
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.os.Bundle import android.os.Bundle
@ -52,11 +54,19 @@ class MainActivity : SplashActivity<ActivityMainMd2Binding>() {
private var isRootFragment = true private var isRootFragment = true
@SuppressLint("InlinedApi")
override fun showMainUI(savedInstanceState: Bundle?) { override fun showMainUI(savedInstanceState: Bundle?) {
setContentView() setContentView()
showUnsupportedMessage() showUnsupportedMessage()
askForHomeShortcut() askForHomeShortcut()
// Ask permission to post notifications for background update check
if (Config.checkUpdate) {
withPermission(Manifest.permission.POST_NOTIFICATIONS) {
Config.checkUpdate = it
}
}
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
navigation.addOnDestinationChangedListener { _, destination, _ -> navigation.addOnDestinationChangedListener { _, destination, _ ->

View File

@ -8,6 +8,7 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableRvItem import com.topjohnwu.magisk.databinding.ObservableRvItem
import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.ktx.activity
import com.topjohnwu.magisk.utils.TextHolder import com.topjohnwu.magisk.utils.TextHolder
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
@ -57,6 +58,8 @@ sealed class BaseSettingsItem : ObservableRvItem() {
set(checked, value, { onPressed(view, handler) }) set(checked, value, { onPressed(view, handler) })
override fun onPressed(view: View, handler: Handler) { override fun onPressed(view: View, handler: Handler) {
// Make sure the checked state is synced
notifyPropertyChanged(BR.checked)
handler.onItemPressed(view, this) { handler.onItemPressed(view, this) {
value = !value value = !value
notifyPropertyChanged(BR.checked) notifyPropertyChanged(BR.checked)
@ -72,7 +75,7 @@ sealed class BaseSettingsItem : ObservableRvItem() {
override fun onPressed(view: View, handler: Handler) { override fun onPressed(view: View, handler: Handler) {
handler.onItemPressed(view, this) { handler.onItemPressed(view, this) {
MagiskDialog(view.context).apply { MagiskDialog(view.activity).apply {
setTitle(title.getText(view.resources)) setTitle(title.getText(view.resources))
setView(getView(view.context)) setView(getView(view.context))
setButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
@ -115,7 +118,7 @@ sealed class BaseSettingsItem : ObservableRvItem() {
override fun onPressed(view: View, handler: Handler) { override fun onPressed(view: View, handler: Handler) {
handler.onItemPressed(view, this) { handler.onItemPressed(view, this) {
MagiskDialog(view.context).apply { MagiskDialog(view.activity).apply {
setTitle(title.getText(view.resources)) setTitle(title.getText(view.resources))
setButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = android.R.string.cancel text = android.R.string.cancel

View File

@ -23,6 +23,7 @@ import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.ktx.activity
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.asText import com.topjohnwu.magisk.utils.asText
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
@ -111,7 +112,7 @@ object Restore : BaseSettingsItem.Blank() {
override fun onPressed(view: View, handler: Handler) { override fun onPressed(view: View, handler: Handler) {
handler.onItemPressed(view, this) { handler.onItemPressed(view, this) {
MagiskDialog(view.context).apply { MagiskDialog(view.activity).apply {
setTitle(R.string.settings_restore_app_title) setTitle(R.string.settings_restore_app_title)
setMessage(R.string.restore_app_confirmation) setMessage(R.string.restore_app_confirmation)
setButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {

View File

@ -96,6 +96,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
override fun onItemPressed(view: View, item: BaseSettingsItem, andThen: () -> Unit) { override fun onItemPressed(view: View, item: BaseSettingsItem, andThen: () -> Unit) {
when (item) { when (item) {
DownloadPath -> withExternalRW(andThen) DownloadPath -> withExternalRW(andThen)
UpdateChecker -> withPostNotificationPermission(andThen)
Biometrics -> authenticate(andThen) Biometrics -> authenticate(andThen)
Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate() Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate()
DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate() DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate()

View File

@ -1,6 +1,6 @@
package com.topjohnwu.magisk.view package com.topjohnwu.magisk.view
import android.content.Context import android.app.Activity
import android.content.DialogInterface import android.content.DialogInterface
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@ -21,22 +21,26 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.databinding.* import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.view.MagiskDialog.DialogClickListener import com.topjohnwu.magisk.view.MagiskDialog.DialogClickListener
typealias DialogButtonClickListener = (DialogInterface) -> Unit typealias DialogButtonClickListener = (DialogInterface) -> Unit
class MagiskDialog( class MagiskDialog(
context: Context, theme: Int = 0 context: Activity, theme: Int = 0
) : AppCompatDialog(context, theme) { ) : AppCompatDialog(context, theme) {
private val binding: DialogMagiskBaseBinding = private val binding: DialogMagiskBaseBinding =
DialogMagiskBaseBinding.inflate(LayoutInflater.from(context)) DialogMagiskBaseBinding.inflate(LayoutInflater.from(context))
private val data = Data() private val data = Data()
val activity: BaseActivity get() = ownerActivity as BaseActivity
init { init {
binding.setVariable(BR.data, data) binding.setVariable(BR.data, data)
setCancelable(true) setCancelable(true)
setOwnerActivity(context)
} }
inner class Data : ObservableHost { inner class Data : ObservableHost {

View File

@ -6,7 +6,6 @@ import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION.SDK_INT
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
@ -16,6 +15,7 @@ import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.download.DownloadService import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.ktx.getBitmap import com.topjohnwu.magisk.ktx.getBitmap
import com.topjohnwu.magisk.ktx.selfLaunchIntent
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@ -44,18 +44,10 @@ object Notifications {
} }
} }
fun selfLaunchIntent(context: Context): Intent {
val pm = context.packageManager
val intent = pm.getLaunchIntentForPackage(context.packageName)!!
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
return intent
}
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
fun updateDone(context: Context) { fun updateDone(context: Context) {
setup(context)
val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
val pending = PendingIntent.getActivity(context, 0, selfLaunchIntent(context), flag) val pending = PendingIntent.getActivity(context, 0, context.selfLaunchIntent(), flag)
val builder = if (SDK_INT >= Build.VERSION_CODES.O) { val builder = if (SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context, UPDATED_CHANNEL) Notification.Builder(context, UPDATED_CHANNEL)
.setSmallIcon(context.getBitmap(R.drawable.ic_magisk_outline).toIcon()) .setSmallIcon(context.getBitmap(R.drawable.ic_magisk_outline).toIcon())
@ -72,7 +64,6 @@ object Notifications {
fun updateAvailable(context: Context) { fun updateAvailable(context: Context) {
val intent = DownloadService.getPendingIntent(context, Subject.App()) val intent = DownloadService.getPendingIntent(context, Subject.App())
val bitmap = context.getBitmap(R.drawable.ic_magisk_outline) val bitmap = context.getBitmap(R.drawable.ic_magisk_outline)
val builder = if (SDK_INT >= Build.VERSION_CODES.O) { val builder = if (SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context, UPDATE_CHANNEL) Notification.Builder(context, UPDATE_CHANNEL)
@ -90,7 +81,7 @@ object Notifications {
mgr.notify(APP_UPDATE_NOTIFICATION_ID, builder.build()) mgr.notify(APP_UPDATE_NOTIFICATION_ID, builder.build())
} }
fun progress(context: Context, title: CharSequence): Notification.Builder { fun startProgress(context: Context, title: CharSequence): Notification.Builder {
val builder = if (SDK_INT >= Build.VERSION_CODES.O) { val builder = if (SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context, PROGRESS_CHANNEL) Notification.Builder(context, PROGRESS_CHANNEL)
} else { } else {

View File

@ -230,6 +230,7 @@
<string name="unsupport_nonroot_stub_msg">The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK.</string> <string name="unsupport_nonroot_stub_msg">The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK.</string>
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string> <string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
<string name="external_rw_permission_denied">Grant storage permission to enable this functionality</string> <string name="external_rw_permission_denied">Grant storage permission to enable this functionality</string>
<string name="post_notifications_denied">Grant notifications permission to enable this functionality</string>
<string name="install_unknown_denied">Allow "install unknown apps" to enable this functionality</string> <string name="install_unknown_denied">Allow "install unknown apps" to enable this functionality</string>
<string name="add_shortcut_title">Add shortcut to home screen</string> <string name="add_shortcut_title">Add shortcut to home screen</string>
<string name="add_shortcut_msg">After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen?</string> <string name="add_shortcut_msg">After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen?</string>

View File

@ -43,13 +43,13 @@ private val Project.android: BaseAppModuleExtension
fun Project.setupCommon() { fun Project.setupCommon() {
androidBase { androidBase {
compileSdkVersion(32) compileSdkVersion(33)
buildToolsVersion = "32.0.0" buildToolsVersion = "32.0.0"
ndkPath = "$sdkDirectory/ndk/magisk" ndkPath = "$sdkDirectory/ndk/magisk"
defaultConfig { defaultConfig {
minSdk = 21 minSdk = 21
targetSdk = 32 targetSdk = 33
} }
compileOptions { compileOptions {