mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-23 18:15:30 +00:00
parent
3f7f6e619a
commit
928a16d8cc
@ -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"
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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)
|
||||||
|
@ -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() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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, _ ->
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user