Migrate a lot of classes to Kotlin
This commit is contained in:
parent
00bff4912e
commit
326eee8c83
@ -1,7 +0,0 @@
|
||||
package com.topjohnwu.magisk.ui.base
|
||||
|
||||
import android.content.Intent
|
||||
|
||||
interface ActivityResultListener {
|
||||
fun onActivityResult(resultCode: Int, data: Intent?)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package com.topjohnwu.magisk.ui.base
|
||||
|
||||
import android.content.Intent
|
||||
|
||||
interface IBaseLeanback {
|
||||
|
||||
fun runWithExternalRW(callback: Runnable)
|
||||
fun runWithPermissions(vararg permissions: String, callback: Runnable)
|
||||
fun startActivityForResult(intent: Intent, requestCode: Int, listener: ActivityResultListener)
|
||||
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
package com.topjohnwu.magisk.ui.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -17,6 +19,7 @@ import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import com.ncapdevi.fragnav.FragNavController
|
||||
import com.ncapdevi.fragnav.FragNavTransactionOptions
|
||||
import com.skoumal.teanity.view.TeanityActivity
|
||||
import com.skoumal.teanity.viewevents.ViewEvent
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.model.events.BackPressEvent
|
||||
@ -28,16 +31,20 @@ import com.topjohnwu.magisk.model.navigation.Navigator
|
||||
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
|
||||
import com.topjohnwu.magisk.utils.LocaleManager
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import timber.log.Timber
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
typealias RequestCallback = MagiskActivity<*, *>.(Int, Intent?) -> Unit
|
||||
|
||||
abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBinding> :
|
||||
MagiskLeanbackActivity<ViewModel, Binding>(), FragNavController.RootFragmentListener,
|
||||
Navigator, FragNavController.TransactionListener {
|
||||
TeanityActivity<ViewModel, Binding>(), FragNavController.RootFragmentListener,
|
||||
Navigator, FragNavController.TransactionListener {
|
||||
|
||||
override val numberOfRootFragments: Int get() = baseFragments.size
|
||||
override val baseFragments: List<KClass<out Fragment>> = listOf()
|
||||
private val resultCallbacks = SparseArrayCompat<RequestCallback>()
|
||||
|
||||
|
||||
protected open val defaultPosition: Int = 0
|
||||
|
||||
@ -188,19 +195,23 @@ abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBin
|
||||
Dexter.withActivity(this)
|
||||
.withPermissions(*permissions)
|
||||
.withListener(object : MultiplePermissionsListener {
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) =
|
||||
if (report?.areAllPermissionsGranted() == true) {
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
|
||||
if (report.areAllPermissionsGranted()) {
|
||||
request.onSuccess()
|
||||
} else {
|
||||
request.onFailure()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPermissionRationaleShouldBeShown(
|
||||
permissions: MutableList<PermissionRequest>?,
|
||||
token: PermissionToken?
|
||||
) = request.onShowRationale(permissions.orEmpty().map { it.name })
|
||||
})
|
||||
.check()
|
||||
permissions: MutableList<PermissionRequest>,
|
||||
token: PermissionToken
|
||||
) = token.continuePermissionRequest()
|
||||
}).check()
|
||||
}
|
||||
|
||||
fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) {
|
||||
withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder)
|
||||
}
|
||||
|
||||
private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) =
|
||||
@ -210,4 +221,21 @@ abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBin
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
resultCallbacks[requestCode]?.apply {
|
||||
resultCallbacks.remove(requestCode)
|
||||
invoke(this@MagiskActivity, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
fun startActivityForResult(
|
||||
intent: Intent,
|
||||
requestCode: Int,
|
||||
listener: RequestCallback
|
||||
) {
|
||||
resultCallbacks[requestCode] = listener
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
package com.topjohnwu.magisk.ui.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import com.karumi.dexter.Dexter
|
||||
import com.karumi.dexter.MultiplePermissionsReport
|
||||
import com.karumi.dexter.PermissionToken
|
||||
import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import com.skoumal.teanity.view.TeanityActivity
|
||||
import com.topjohnwu.magisk.Const
|
||||
|
||||
abstract class MagiskLeanbackActivity<ViewModel : MagiskViewModel, Binding : ViewDataBinding> :
|
||||
TeanityActivity<ViewModel, Binding>(), IBaseLeanback {
|
||||
|
||||
private val resultListeners = SparseArrayCompat<ActivityResultListener>()
|
||||
|
||||
@Deprecated("Permissions will be checked in a different streamlined way")
|
||||
fun runWithExternalRW(callback: () -> Unit) = runWithExternalRW(Runnable { callback() })
|
||||
|
||||
@Deprecated("Permissions will be checked in a different streamlined way")
|
||||
override fun runWithExternalRW(callback: Runnable) {
|
||||
runWithPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, callback = callback)
|
||||
}
|
||||
|
||||
@Deprecated("Permissions will be checked in a different streamlined way")
|
||||
override fun runWithPermissions(vararg permissions: String, callback: Runnable) {
|
||||
Dexter.withActivity(this)
|
||||
.withPermissions(*permissions)
|
||||
.withListener(object : MultiplePermissionsListener {
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||
if (report?.areAllPermissionsGranted() == true) {
|
||||
Const.EXTERNAL_PATH.mkdirs()
|
||||
callback.run()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPermissionRationaleShouldBeShown(
|
||||
permissions: MutableList<PermissionRequest>?,
|
||||
token: PermissionToken?
|
||||
) = Unit
|
||||
})
|
||||
.check()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
resultListeners.get(requestCode)?.apply {
|
||||
resultListeners.remove(requestCode)
|
||||
onActivityResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun startActivityForResult(
|
||||
intent: Intent,
|
||||
requestCode: Int,
|
||||
listener: ActivityResultListener
|
||||
) {
|
||||
resultListeners.put(requestCode, listener)
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
}
|
@ -85,10 +85,12 @@ class ModulesFragment : MagiskFragment<ModuleViewModel, FragmentModulesBinding>(
|
||||
}
|
||||
|
||||
private fun selectFile() {
|
||||
magiskActivity.runWithExternalRW {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "application/zip"
|
||||
startActivityForResult(intent, Const.ID.FETCH_ZIP)
|
||||
magiskActivity.withExternalRW {
|
||||
onSuccess {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "application/zip"
|
||||
startActivityForResult(intent, Const.ID.FETCH_ZIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,20 +74,22 @@ class ReposFragment : MagiskFragment<ModuleViewModel, FragmentReposBinding>(),
|
||||
}
|
||||
|
||||
private fun openChangelog(item: Repo) {
|
||||
MarkDownWindow.show(context, null, item.detailUrl)
|
||||
MarkDownWindow.show(requireActivity(), null, item.detailUrl)
|
||||
}
|
||||
|
||||
private fun installModule(item: Repo) {
|
||||
val context = magiskActivity
|
||||
|
||||
fun download(install: Boolean) {
|
||||
context.runWithExternalRW {
|
||||
val intent = Intent(activity, ClassMap[DownloadModuleService::class.java])
|
||||
.putExtra("repo", item).putExtra("install", install)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent) //hmm, service starts itself in foreground, this seems unnecessary
|
||||
} else {
|
||||
context.startService(intent)
|
||||
context.withExternalRW {
|
||||
onSuccess {
|
||||
val intent = Intent(activity, ClassMap[DownloadModuleService::class.java])
|
||||
.putExtra("repo", item).putExtra("install", install)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import androidx.databinding.ObservableList
|
||||
import com.skoumal.teanity.extensions.subscribeK
|
||||
import com.skoumal.teanity.util.DiffObservableList
|
||||
@ -76,4 +77,8 @@ fun <T1> ObservableList<T1>.copyNewInputInto(
|
||||
val addedValues = sender?.slice(positionStart until positionEnd).orEmpty()
|
||||
target.addAll(addedValues)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
operator fun <E> SparseArrayCompat<E>.set(key: Int, value: E) {
|
||||
put(key, value)
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
package com.topjohnwu.magisk.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.net.ResponseListener;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
|
||||
import ru.noties.markwon.Markwon;
|
||||
import ru.noties.markwon.html.HtmlPlugin;
|
||||
import ru.noties.markwon.image.ImagesPlugin;
|
||||
import ru.noties.markwon.image.svg.SvgPlugin;
|
||||
|
||||
public class MarkDownWindow {
|
||||
|
||||
public static void show(Context activity, String title, String url) {
|
||||
Networking.get(url).getAsString(new Listener(activity, title));
|
||||
}
|
||||
|
||||
public static void show(Context activity, String title, InputStream is) {
|
||||
try (Scanner s = new Scanner(is, "UTF-8")) {
|
||||
s.useDelimiter("\\A");
|
||||
new Listener(activity, title).onResponse(s.next());
|
||||
}
|
||||
}
|
||||
|
||||
static class Listener implements ResponseListener<String> {
|
||||
|
||||
Context activity;
|
||||
String title;
|
||||
|
||||
Listener(Context a, String t) {
|
||||
activity = a;
|
||||
title = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(String md) {
|
||||
Markwon markwon = Markwon.builder(activity)
|
||||
.usePlugin(HtmlPlugin.create())
|
||||
.usePlugin(ImagesPlugin.create(activity))
|
||||
.usePlugin(SvgPlugin.create(activity.getResources()))
|
||||
.build();
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||
alert.setTitle(title);
|
||||
View mv = LayoutInflater.from(activity).inflate(R.layout.markdown_window, null);
|
||||
TextView tv = mv.findViewById(R.id.md_txt);
|
||||
try {
|
||||
markwon.setMarkdown(tv, md);
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
//Nothing we can do about this error other than show error message
|
||||
tv.setText(R.string.download_file_error);
|
||||
}
|
||||
alert.setView(mv);
|
||||
alert.setNegativeButton(R.string.close, (dialog, id) -> dialog.dismiss());
|
||||
alert.show();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.topjohnwu.magisk.view
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.net.Networking
|
||||
import com.topjohnwu.net.ResponseListener
|
||||
import ru.noties.markwon.Markwon
|
||||
import ru.noties.markwon.html.HtmlPlugin
|
||||
import ru.noties.markwon.image.ImagesPlugin
|
||||
import ru.noties.markwon.image.svg.SvgPlugin
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
|
||||
object MarkDownWindow {
|
||||
|
||||
fun show(activity: Context, title: String?, url: String) {
|
||||
Networking.get(url).getAsString(Listener(activity, title))
|
||||
}
|
||||
|
||||
fun show(activity: Context, title: String?, input: InputStream) {
|
||||
Scanner(input, "UTF-8").use {
|
||||
it.useDelimiter("\\A")
|
||||
Listener(activity, title).onResponse(it.next())
|
||||
}
|
||||
}
|
||||
|
||||
internal class Listener(var activity: Context, var title: String?) : ResponseListener<String> {
|
||||
|
||||
override fun onResponse(md: String) {
|
||||
val markwon = Markwon.builder(activity)
|
||||
.usePlugin(HtmlPlugin.create())
|
||||
.usePlugin(ImagesPlugin.create(activity))
|
||||
.usePlugin(SvgPlugin.create(activity.resources))
|
||||
.build()
|
||||
val alert = AlertDialog.Builder(activity)
|
||||
alert.setTitle(title)
|
||||
val mv = LayoutInflater.from(activity).inflate(R.layout.markdown_window, null)
|
||||
val tv = mv.findViewById<TextView>(R.id.md_txt)
|
||||
try {
|
||||
markwon.setMarkdown(tv, md)
|
||||
} catch (e: ExceptionInInitializerError) {
|
||||
//Nothing we can do about this error other than show error message
|
||||
tv.setText(R.string.download_file_error)
|
||||
}
|
||||
|
||||
alert.setView(mv)
|
||||
alert.setNegativeButton(R.string.close) { dialog, _ -> dialog.dismiss() }
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package com.topjohnwu.magisk.view;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.net.DownloadProgressListener;
|
||||
|
||||
public class ProgressNotification implements DownloadProgressListener {
|
||||
|
||||
private NotificationCompat.Builder builder;
|
||||
private Notification notification;
|
||||
private long prevTime;
|
||||
|
||||
public ProgressNotification(String title) {
|
||||
builder = Notifications.progress(title);
|
||||
prevTime = System.currentTimeMillis();
|
||||
update();
|
||||
Utils.INSTANCE.toast(App.self.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesDownloaded, long totalBytes) {
|
||||
long cur = System.currentTimeMillis();
|
||||
if (cur - prevTime >= 1000) {
|
||||
prevTime = cur;
|
||||
int progress = (int) (bytesDownloaded * 100 / totalBytes);
|
||||
builder.setProgress(100, progress, false);
|
||||
builder.setContentText(progress + "%");
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public NotificationCompat.Builder getNotificationBuilder() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public Notification getNotification() {
|
||||
return notification;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
notification = builder.build();
|
||||
Notifications.mgr.notify(hashCode(), notification);
|
||||
}
|
||||
|
||||
private void lastUpdate() {
|
||||
notification = builder.build();
|
||||
Notifications.mgr.cancel(hashCode());
|
||||
Notifications.mgr.notify(notification.hashCode(), notification);
|
||||
}
|
||||
|
||||
public void dlDone() {
|
||||
dlDone(PendingIntent.getActivity(App.self, hashCode(),
|
||||
new Intent(), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
}
|
||||
|
||||
public void dlDone(PendingIntent intent) {
|
||||
builder.setProgress(0, 0, false)
|
||||
.setContentText(App.self.getString(R.string.download_complete))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setContentIntent(intent)
|
||||
.setOngoing(false)
|
||||
.setAutoCancel(true);
|
||||
lastUpdate();
|
||||
}
|
||||
|
||||
public void dlFail() {
|
||||
builder.setProgress(0, 0, false)
|
||||
.setContentText(App.self.getString(R.string.download_file_error))
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setOngoing(false);
|
||||
lastUpdate();
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
Notifications.mgr.cancel(hashCode());
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.topjohnwu.magisk.view
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
|
||||
import androidx.core.app.NotificationCompat
|
||||
|
||||
import com.topjohnwu.magisk.App
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.net.DownloadProgressListener
|
||||
|
||||
class ProgressNotification(title: String) : DownloadProgressListener {
|
||||
|
||||
val notificationBuilder: NotificationCompat.Builder = Notifications.progress(title)
|
||||
lateinit var notification: Notification
|
||||
private set
|
||||
private var prevTime: Long = 0
|
||||
|
||||
init {
|
||||
prevTime = System.currentTimeMillis()
|
||||
update()
|
||||
Utils.toast(App.self.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT)
|
||||
}
|
||||
|
||||
override fun onProgress(bytesDownloaded: Long, totalBytes: Long) {
|
||||
val cur = System.currentTimeMillis()
|
||||
if (cur - prevTime >= 1000) {
|
||||
prevTime = cur
|
||||
val progress = (bytesDownloaded * 100 / totalBytes).toInt()
|
||||
notificationBuilder.setProgress(100, progress, false)
|
||||
notificationBuilder.setContentText("$progress%")
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
fun update() {
|
||||
notification = notificationBuilder.build()
|
||||
Notifications.mgr.notify(hashCode(), notification)
|
||||
}
|
||||
|
||||
private fun lastUpdate() {
|
||||
Notifications.mgr.cancel(hashCode())
|
||||
notification = notificationBuilder.build().apply {
|
||||
Notifications.mgr.notify(hashCode(), this)
|
||||
}
|
||||
}
|
||||
|
||||
fun dlDone(intent: PendingIntent = PendingIntent.getActivity(App.self, hashCode(),
|
||||
Intent(), PendingIntent.FLAG_UPDATE_CURRENT)) {
|
||||
notificationBuilder.setProgress(0, 0, false)
|
||||
.setContentText(App.self.getString(R.string.download_complete))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setContentIntent(intent)
|
||||
.setOngoing(false)
|
||||
.setAutoCancel(true)
|
||||
lastUpdate()
|
||||
}
|
||||
|
||||
fun dlFail() {
|
||||
notificationBuilder.setProgress(0, 0, false)
|
||||
.setContentText(App.self.getString(R.string.download_file_error))
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setOngoing(false)
|
||||
lastUpdate()
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
Notifications.mgr.cancel(hashCode())
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package com.topjohnwu.magisk.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.XAndroidKt;
|
||||
|
||||
public class SnackbarMaker {
|
||||
|
||||
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
||||
View view = activity.findViewById(android.R.id.content);
|
||||
return make(view, text, duration);
|
||||
}
|
||||
|
||||
public static Snackbar make(Activity activity, @StringRes int resId, int duration) {
|
||||
return make(activity, activity.getString(resId), duration);
|
||||
}
|
||||
|
||||
public static Snackbar make(View view, CharSequence text, int duration) {
|
||||
Snackbar snack = Snackbar.make(view, text, duration);
|
||||
setup(snack);
|
||||
return snack;
|
||||
}
|
||||
|
||||
public static Snackbar make(View view, @StringRes int resId, int duration) {
|
||||
Snackbar snack = Snackbar.make(view, resId, duration);
|
||||
setup(snack);
|
||||
return snack;
|
||||
}
|
||||
|
||||
private static void setup(Snackbar snack) {
|
||||
TextView text = snack.getView().findViewById(com.google.android.material.R.id.snackbar_text);
|
||||
text.setMaxLines(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static void showUri(Activity activity, Uri uri) {
|
||||
make(activity, activity.getString(R.string.internal_storage,
|
||||
"/Download/" + XAndroidKt.getFileName(uri)),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.ok, (v)->{}).show();
|
||||
}
|
||||
}
|
46
app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.kt
Normal file
46
app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package com.topjohnwu.magisk.view
|
||||
|
||||
import android.app.Activity
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.utils.fileName
|
||||
|
||||
object SnackbarMaker {
|
||||
|
||||
fun make(activity: Activity, text: CharSequence, duration: Int): Snackbar {
|
||||
val view = activity.findViewById<View>(android.R.id.content)
|
||||
return make(view, text, duration)
|
||||
}
|
||||
|
||||
fun make(activity: Activity, @StringRes resId: Int, duration: Int): Snackbar {
|
||||
return make(activity, activity.getString(resId), duration)
|
||||
}
|
||||
|
||||
fun make(view: View, text: CharSequence, duration: Int): Snackbar {
|
||||
val snack = Snackbar.make(view, text, duration)
|
||||
setup(snack)
|
||||
return snack
|
||||
}
|
||||
|
||||
fun make(view: View, @StringRes resId: Int, duration: Int): Snackbar {
|
||||
val snack = Snackbar.make(view, resId, duration)
|
||||
setup(snack)
|
||||
return snack
|
||||
}
|
||||
|
||||
private fun setup(snack: Snackbar) {
|
||||
val text = snack.view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
|
||||
text.maxLines = Integer.MAX_VALUE
|
||||
}
|
||||
|
||||
fun showUri(activity: Activity, uri: Uri) {
|
||||
make(activity, activity.getString(R.string.internal_storage,
|
||||
"/Download/" + uri.fileName),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.ok) { }.show()
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package com.topjohnwu.magisk.view.dialogs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.topjohnwu.magisk.databinding.AlertDialogBinding;
|
||||
|
||||
public class CustomAlertDialog extends AlertDialog.Builder {
|
||||
|
||||
private DialogInterface.OnClickListener positiveListener;
|
||||
private DialogInterface.OnClickListener negativeListener;
|
||||
private DialogInterface.OnClickListener neutralListener;
|
||||
|
||||
protected AlertDialog dialog;
|
||||
protected AlertDialogBinding binding;
|
||||
|
||||
{
|
||||
binding = AlertDialogBinding.inflate(LayoutInflater.from(getContext()));
|
||||
super.setView(binding.getRoot());
|
||||
binding.message.setVisibility(View.GONE);
|
||||
binding.negative.setVisibility(View.GONE);
|
||||
binding.positive.setVisibility(View.GONE);
|
||||
binding.neutral.setVisibility(View.GONE);
|
||||
binding.buttonPanel.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public CustomAlertDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CustomAlertDialog(@NonNull Context context, @StyleRes int themeResId) {
|
||||
super(context, themeResId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setView(int layoutResId) { return this; }
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setView(View view) { return this; }
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setMessage(@Nullable CharSequence message) {
|
||||
binding.message.setVisibility(View.VISIBLE);
|
||||
binding.message.setText(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setMessage(@StringRes int messageId) {
|
||||
return setMessage(getContext().getString(messageId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||
binding.buttonPanel.setVisibility(View.VISIBLE);
|
||||
binding.positive.setVisibility(View.VISIBLE);
|
||||
binding.positive.setText(text);
|
||||
positiveListener = listener;
|
||||
binding.positive.setOnClickListener(v -> {
|
||||
if (positiveListener != null) {
|
||||
positiveListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setPositiveButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||
return setPositiveButton(getContext().getString(textId), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||
binding.buttonPanel.setVisibility(View.VISIBLE);
|
||||
binding.negative.setVisibility(View.VISIBLE);
|
||||
binding.negative.setText(text);
|
||||
negativeListener = listener;
|
||||
binding.negative.setOnClickListener(v -> {
|
||||
if (negativeListener != null) {
|
||||
negativeListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setNegativeButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||
return setNegativeButton(getContext().getString(textId), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||
binding.buttonPanel.setVisibility(View.VISIBLE);
|
||||
binding.neutral.setVisibility(View.VISIBLE);
|
||||
binding.neutral.setText(text);
|
||||
neutralListener = listener;
|
||||
binding.neutral.setOnClickListener(v -> {
|
||||
if (neutralListener != null) {
|
||||
neutralListener.onClick(dialog, DialogInterface.BUTTON_NEUTRAL);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAlertDialog setNeutralButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||
return setNeutralButton(getContext().getString(textId), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog create() {
|
||||
dialog = super.create();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog show() {
|
||||
create();
|
||||
dialog.show();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package com.topjohnwu.magisk.view.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
||||
import com.topjohnwu.magisk.databinding.AlertDialogBinding
|
||||
|
||||
open class CustomAlertDialog : AlertDialog.Builder {
|
||||
|
||||
private var positiveListener: DialogInterface.OnClickListener? = null
|
||||
private var negativeListener: DialogInterface.OnClickListener? = null
|
||||
private var neutralListener: DialogInterface.OnClickListener? = null
|
||||
|
||||
protected var dialog: AlertDialog? = null
|
||||
protected var binding: AlertDialogBinding =
|
||||
AlertDialogBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
init {
|
||||
super.setView(binding.root)
|
||||
binding.message.visibility = View.GONE
|
||||
binding.negative.visibility = View.GONE
|
||||
binding.positive.visibility = View.GONE
|
||||
binding.neutral.visibility = View.GONE
|
||||
binding.buttonPanel.visibility = View.GONE
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, @StyleRes themeResId: Int) : super(context, themeResId)
|
||||
|
||||
override fun setView(layoutResId: Int): CustomAlertDialog {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setView(view: View): CustomAlertDialog {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setMessage(message: CharSequence?): CustomAlertDialog {
|
||||
binding.message.visibility = View.VISIBLE
|
||||
binding.message.text = message
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setMessage(@StringRes messageId: Int): CustomAlertDialog {
|
||||
return setMessage(context.getString(messageId))
|
||||
}
|
||||
|
||||
override fun setPositiveButton(text: CharSequence, listener: DialogInterface.OnClickListener?): CustomAlertDialog {
|
||||
binding.buttonPanel.visibility = View.VISIBLE
|
||||
binding.positive.visibility = View.VISIBLE
|
||||
binding.positive.text = text
|
||||
positiveListener = listener
|
||||
binding.positive.setOnClickListener {
|
||||
positiveListener?.onClick(dialog, DialogInterface.BUTTON_POSITIVE)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setPositiveButton(@StringRes textId: Int, listener: DialogInterface.OnClickListener?): CustomAlertDialog {
|
||||
return setPositiveButton(context.getString(textId), listener)
|
||||
}
|
||||
|
||||
override fun setNegativeButton(text: CharSequence, listener: DialogInterface.OnClickListener?): CustomAlertDialog {
|
||||
binding.buttonPanel.visibility = View.VISIBLE
|
||||
binding.negative.visibility = View.VISIBLE
|
||||
binding.negative.text = text
|
||||
negativeListener = listener
|
||||
binding.negative.setOnClickListener {
|
||||
negativeListener?.onClick(dialog, DialogInterface.BUTTON_NEGATIVE)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setNegativeButton(@StringRes textId: Int, listener: DialogInterface.OnClickListener?): CustomAlertDialog {
|
||||
return setNegativeButton(context.getString(textId), listener)
|
||||
}
|
||||
|
||||
override fun setNeutralButton(text: CharSequence, listener: DialogInterface.OnClickListener?): CustomAlertDialog {
|
||||
binding.buttonPanel.visibility = View.VISIBLE
|
||||
binding.neutral.visibility = View.VISIBLE
|
||||
binding.neutral.text = text
|
||||
neutralListener = listener
|
||||
binding.neutral.setOnClickListener {
|
||||
neutralListener?.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setNeutralButton(@StringRes textId: Int, listener: DialogInterface.OnClickListener?): CustomAlertDialog {
|
||||
return setNeutralButton(context.getString(textId), listener)
|
||||
}
|
||||
|
||||
override fun create(): AlertDialog {
|
||||
return super.create().apply { dialog = this }
|
||||
}
|
||||
|
||||
override fun show(): AlertDialog {
|
||||
return create().apply { show() }
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
package com.topjohnwu.magisk.view.dialogs;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Build;
|
||||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public class FingerprintAuthDialog extends CustomAlertDialog {
|
||||
|
||||
private final Runnable callback;
|
||||
@Nullable
|
||||
private Runnable failureCallback;
|
||||
private DialogFingerprintHelper helper;
|
||||
|
||||
public FingerprintAuthDialog(@NonNull Activity activity, @NonNull Runnable onSuccess) {
|
||||
super(activity);
|
||||
callback = onSuccess;
|
||||
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
|
||||
fingerprint.setBounds(0, 0, Utils.INSTANCE.dpInPx(50), Utils.INSTANCE.dpInPx(50));
|
||||
Resources.Theme theme = activity.getTheme();
|
||||
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
|
||||
fingerprint.setTint(ta.getColor(0, Color.GRAY));
|
||||
ta.recycle();
|
||||
binding.message.setCompoundDrawables(null, null, null, fingerprint);
|
||||
binding.message.setCompoundDrawablePadding(Utils.INSTANCE.dpInPx(20));
|
||||
binding.message.setGravity(Gravity.CENTER);
|
||||
setMessage(R.string.auth_fingerprint);
|
||||
setNegativeButton(R.string.close, (d, w) -> {
|
||||
helper.cancel();
|
||||
if (failureCallback != null) {
|
||||
failureCallback.run();
|
||||
}
|
||||
});
|
||||
setOnCancelListener(d -> {
|
||||
helper.cancel();
|
||||
if (failureCallback != null) {
|
||||
failureCallback.run();
|
||||
}
|
||||
});
|
||||
try {
|
||||
helper = new DialogFingerprintHelper();
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public FingerprintAuthDialog(@NonNull Activity activity, @NonNull Runnable onSuccess, @NonNull Runnable onFailure) {
|
||||
this(activity, onSuccess);
|
||||
failureCallback = onFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog show() {
|
||||
create();
|
||||
if (helper == null) {
|
||||
dialog.dismiss();
|
||||
Utils.INSTANCE.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
|
||||
} else {
|
||||
helper.authenticate();
|
||||
dialog.show();
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
class DialogFingerprintHelper extends FingerprintHelper {
|
||||
|
||||
DialogFingerprintHelper() throws Exception {}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
||||
binding.message.setTextColor(Color.RED);
|
||||
binding.message.setText(errString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
||||
binding.message.setTextColor(Color.RED);
|
||||
binding.message.setText(helpString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
binding.message.setTextColor(Color.RED);
|
||||
binding.message.setText(R.string.auth_fail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
||||
dismiss();
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.topjohnwu.magisk.view.dialogs
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.utils.FingerprintHelper
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
class FingerprintAuthDialog(activity: Activity, private val callback: () -> Unit)
|
||||
: CustomAlertDialog(activity) {
|
||||
|
||||
private var failureCallback: (() -> Unit)? = null
|
||||
private var helper: DialogFingerprintHelper? = null
|
||||
|
||||
init {
|
||||
val fingerprint = ContextCompat.getDrawable(activity, R.drawable.ic_fingerprint)
|
||||
fingerprint?.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50))
|
||||
val theme = activity.theme
|
||||
val ta = theme.obtainStyledAttributes(intArrayOf(R.attr.imageColorTint))
|
||||
fingerprint?.setTint(ta.getColor(0, Color.GRAY))
|
||||
ta.recycle()
|
||||
binding.message.setCompoundDrawables(null, null, null, fingerprint)
|
||||
binding.message.compoundDrawablePadding = Utils.dpInPx(20)
|
||||
binding.message.gravity = Gravity.CENTER
|
||||
setMessage(R.string.auth_fingerprint)
|
||||
setNegativeButton(R.string.close) { _, _ ->
|
||||
helper?.cancel()
|
||||
failureCallback?.invoke()
|
||||
}
|
||||
setOnCancelListener {
|
||||
helper?.cancel()
|
||||
failureCallback?.invoke()
|
||||
}
|
||||
runCatching {
|
||||
helper = DialogFingerprintHelper()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
constructor(activity: Activity, onSuccess: () -> Unit, onFailure: () -> Unit)
|
||||
: this(activity, onSuccess) {
|
||||
failureCallback = onFailure
|
||||
}
|
||||
|
||||
override fun show(): AlertDialog {
|
||||
return create().apply {
|
||||
if (helper == null) {
|
||||
dismiss()
|
||||
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT)
|
||||
} else {
|
||||
helper?.authenticate()
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inner class DialogFingerprintHelper @Throws(Exception::class)
|
||||
constructor() : FingerprintHelper() {
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||
binding.message.setTextColor(Color.RED)
|
||||
binding.message.text = errString
|
||||
}
|
||||
|
||||
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
|
||||
binding.message.setTextColor(Color.RED)
|
||||
binding.message.text = helpString
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
binding.message.setTextColor(Color.RED)
|
||||
binding.message.setText(R.string.auth_fail)
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
|
||||
dismiss()
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package com.topjohnwu.magisk.view.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.Info;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.ui.base.IBaseLeanback;
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.ProgressNotification;
|
||||
import com.topjohnwu.magisk.view.SnackbarMaker;
|
||||
import com.topjohnwu.net.Networking;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
class InstallMethodDialog extends AlertDialog.Builder {
|
||||
|
||||
<Ctxt extends Activity & IBaseLeanback> InstallMethodDialog(Ctxt activity, List<String> options) {
|
||||
super(activity);
|
||||
setTitle(R.string.select_method);
|
||||
setItems(options.toArray(new String[0]), (dialog, idx) -> {
|
||||
Intent intent;
|
||||
switch (idx) {
|
||||
case 1:
|
||||
patchBoot(activity);
|
||||
break;
|
||||
case 0:
|
||||
downloadOnly(activity);
|
||||
break;
|
||||
case 2:
|
||||
intent = new Intent(activity, ClassMap.get(FlashActivity.class))
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
||||
activity.startActivity(intent);
|
||||
break;
|
||||
case 3:
|
||||
installInactiveSlot(activity);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <Ctxt extends Activity & IBaseLeanback> void patchBoot(Ctxt activity) {
|
||||
activity.runWithExternalRW(() -> {
|
||||
Utils.INSTANCE.toast(R.string.patch_file_msg, Toast.LENGTH_LONG);
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
|
||||
.setType("*/*")
|
||||
.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT,
|
||||
(resultCode, data) -> {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
Intent i = new Intent(activity, ClassMap.get(FlashActivity.class))
|
||||
.setData(data.getData())
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_FILE);
|
||||
activity.startActivity(i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private <Ctxt extends Activity & IBaseLeanback> void downloadOnly(Ctxt activity) {
|
||||
activity.runWithExternalRW(() -> {
|
||||
String filename = Utils.INSTANCE.fmt("Magisk-v%s(%d).zip",
|
||||
Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode);
|
||||
File zip = new File(Const.EXTERNAL_PATH, filename);
|
||||
ProgressNotification progress = new ProgressNotification(filename);
|
||||
Networking.get(Info.magiskLink)
|
||||
.setDownloadProgressListener(progress)
|
||||
.setErrorHandler((conn, e) -> progress.dlFail())
|
||||
.getAsFile(zip, f -> {
|
||||
progress.dlDone();
|
||||
SnackbarMaker.make(activity,
|
||||
activity.getString(R.string.internal_storage, "/Download/" + filename),
|
||||
Snackbar.LENGTH_LONG).show();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private <Ctxt extends Activity & IBaseLeanback> void installInactiveSlot(Ctxt activity) {
|
||||
new CustomAlertDialog(activity)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(R.string.install_inactive_slot_msg)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.yes, (d, i) -> {
|
||||
Intent intent = new Intent(activity, ClassMap.get(FlashActivity.class))
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
|
||||
activity.startActivity(intent);
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.topjohnwu.magisk.view.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.topjohnwu.magisk.ClassMap
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.ui.base.MagiskActivity
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.ProgressNotification
|
||||
import com.topjohnwu.magisk.view.SnackbarMaker
|
||||
import com.topjohnwu.net.Networking
|
||||
import java.io.File
|
||||
|
||||
internal class InstallMethodDialog(activity: MagiskActivity<*, *>, options: List<String>) : AlertDialog.Builder(activity) {
|
||||
|
||||
init {
|
||||
setTitle(R.string.select_method)
|
||||
setItems(options.toTypedArray()) { _, idx ->
|
||||
when (idx) {
|
||||
0 -> downloadOnly(activity)
|
||||
1 -> patchBoot(activity)
|
||||
2 -> {
|
||||
val intent = Intent(activity, ClassMap[FlashActivity::class.java])
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
3 -> installInactiveSlot(activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun patchBoot(activity: MagiskActivity<*, *>) {
|
||||
activity.withExternalRW {
|
||||
onSuccess {
|
||||
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
.setType("*/*")
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT) { resultCode, data ->
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val i = Intent(this, ClassMap[FlashActivity::class.java])
|
||||
.setData(data.data)
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_FILE)
|
||||
startActivity(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadOnly(activity: MagiskActivity<*, *>) {
|
||||
activity.withExternalRW {
|
||||
onSuccess {
|
||||
val filename = "Magisk-v${Info.remoteMagiskVersionString}" +
|
||||
"(${Info.remoteMagiskVersionCode}).zip"
|
||||
val zip = File(Const.EXTERNAL_PATH, filename)
|
||||
val progress = ProgressNotification(filename)
|
||||
Networking.get(Info.magiskLink)
|
||||
.setDownloadProgressListener(progress)
|
||||
.setErrorHandler { _, _ -> progress.dlFail() }
|
||||
.getAsFile(zip) {
|
||||
progress.dlDone()
|
||||
SnackbarMaker.make(activity,
|
||||
activity.getString(R.string.internal_storage, "/Download/$filename"),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun installInactiveSlot(activity: MagiskActivity<*, *>) {
|
||||
CustomAlertDialog(activity)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(R.string.install_inactive_slot_msg)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
val intent = Intent(activity, ClassMap[FlashActivity::class.java])
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show()
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package com.topjohnwu.magisk.view.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.topjohnwu.magisk.Info;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.ui.base.IBaseLeanback;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
import com.topjohnwu.superuser.ShellUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MagiskInstallDialog extends CustomAlertDialog {
|
||||
public <Ctxt extends Activity & IBaseLeanback> MagiskInstallDialog(Ctxt a) {
|
||||
super(a);
|
||||
String filename = Utils.INSTANCE.fmt("Magisk-v%s(%d).zip",
|
||||
Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode);
|
||||
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk)));
|
||||
setMessage(a.getString(R.string.repo_install_msg, filename));
|
||||
setCancelable(true);
|
||||
setPositiveButton(R.string.install, (d, i) -> {
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add(a.getString(R.string.download_zip_only));
|
||||
options.add(a.getString(R.string.select_patch_file));
|
||||
if (Shell.rootAccess()) {
|
||||
options.add(a.getString(R.string.direct_install));
|
||||
String s = ShellUtils.fastCmd("grep_prop ro.build.ab_update");
|
||||
if (!s.isEmpty() && Boolean.parseBoolean(s)) {
|
||||
options.add(a.getString(R.string.install_inactive_slot));
|
||||
}
|
||||
}
|
||||
new InstallMethodDialog(a, options).show();
|
||||
});
|
||||
if (!TextUtils.isEmpty(Info.magiskNoteLink)) {
|
||||
setNeutralButton(R.string.release_notes, (d, i) -> {
|
||||
if (Info.magiskNoteLink.contains("forum.xda-developers")) {
|
||||
// Open forum links in browser
|
||||
Utils.INSTANCE.openLink(a, Uri.parse(Info.magiskNoteLink));
|
||||
} else {
|
||||
MarkDownWindow.show(a, null, Info.magiskNoteLink);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.topjohnwu.magisk.view.dialogs
|
||||
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.ui.base.MagiskActivity
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import java.util.*
|
||||
|
||||
class MagiskInstallDialog(a: MagiskActivity<*, *>) : CustomAlertDialog(a) {
|
||||
init {
|
||||
val filename = "Magisk v${Info.remoteMagiskVersionString}" +
|
||||
"(${Info.remoteMagiskVersionCode})"
|
||||
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk)))
|
||||
setMessage(a.getString(R.string.repo_install_msg, filename))
|
||||
setCancelable(true)
|
||||
setPositiveButton(R.string.install) { _, _ ->
|
||||
val options = ArrayList<String>()
|
||||
options.add(a.getString(R.string.download_zip_only))
|
||||
options.add(a.getString(R.string.select_patch_file))
|
||||
if (Shell.rootAccess()) {
|
||||
options.add(a.getString(R.string.direct_install))
|
||||
val s = ShellUtils.fastCmd("grep_prop ro.build.ab_update")
|
||||
if (s.isNotEmpty() && s.toBoolean()) {
|
||||
options.add(a.getString(R.string.install_inactive_slot))
|
||||
}
|
||||
}
|
||||
InstallMethodDialog(a, options).show()
|
||||
}
|
||||
if (!TextUtils.isEmpty(Info.magiskNoteLink)) {
|
||||
setNeutralButton(R.string.release_notes) { _, _ ->
|
||||
if (Info.magiskNoteLink.contains("forum.xda-developers")) {
|
||||
// Open forum links in browser
|
||||
Utils.openLink(a, Uri.parse(Info.magiskNoteLink))
|
||||
} else {
|
||||
MarkDownWindow.show(a, null, Info.magiskNoteLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.topjohnwu.magisk.view.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.topjohnwu.magisk.Info;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.DownloadApp;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow;
|
||||
|
||||
public class ManagerInstallDialog extends CustomAlertDialog {
|
||||
|
||||
public ManagerInstallDialog(@NonNull Activity a) {
|
||||
super(a);
|
||||
String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)",
|
||||
Info.remoteManagerVersionString, Info.remoteManagerVersionCode);
|
||||
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name)));
|
||||
setMessage(a.getString(R.string.repo_install_msg, name));
|
||||
setCancelable(true);
|
||||
setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name));
|
||||
if (!TextUtils.isEmpty(Info.managerNoteLink)) {
|
||||
setNeutralButton(R.string.app_changelog, (d, i) -> MarkDownWindow.show(a, null, Info.managerNoteLink));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.topjohnwu.magisk.view.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.utils.DownloadApp
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow
|
||||
|
||||
class ManagerInstallDialog(a: Activity) : CustomAlertDialog(a) {
|
||||
|
||||
init {
|
||||
val name = "MagiskManager v${Info.remoteManagerVersionString}" +
|
||||
"(${Info.remoteManagerVersionCode})"
|
||||
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name)))
|
||||
setMessage(a.getString(R.string.repo_install_msg, name))
|
||||
setCancelable(true)
|
||||
setPositiveButton(R.string.install) { _, _ -> DownloadApp.upgrade(name) }
|
||||
if (Info.managerNoteLink.isNotEmpty()) {
|
||||
setNeutralButton(R.string.app_changelog) { _, _ ->
|
||||
MarkDownWindow.show(a, null, Info.managerNoteLink) }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package com.topjohnwu.magisk.view.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.Info;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.view.ProgressNotification;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class UninstallDialog extends CustomAlertDialog {
|
||||
|
||||
public UninstallDialog(@NonNull Activity activity) {
|
||||
super(activity);
|
||||
setTitle(R.string.uninstall_magisk_title);
|
||||
setMessage(R.string.uninstall_magisk_msg);
|
||||
setNeutralButton(R.string.restore_img, (d, i) -> {
|
||||
ProgressDialog dialog = ProgressDialog.show(activity,
|
||||
activity.getString(R.string.restore_img),
|
||||
activity.getString(R.string.restore_img_msg));
|
||||
Shell.su("restore_imgs").submit(result -> {
|
||||
dialog.cancel();
|
||||
if (result.isSuccess()) {
|
||||
Utils.INSTANCE.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
||||
} else {
|
||||
Utils.INSTANCE.toast(R.string.restore_fail, Toast.LENGTH_LONG);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!TextUtils.isEmpty(Info.uninstallerLink)) {
|
||||
setPositiveButton(R.string.complete_uninstall, (d, i) -> {
|
||||
File zip = new File(activity.getFilesDir(), "uninstaller.zip");
|
||||
ProgressNotification progress = new ProgressNotification(zip.getName());
|
||||
Networking.get(Info.uninstallerLink)
|
||||
.setDownloadProgressListener(progress)
|
||||
.setErrorHandler(((conn, e) -> progress.dlFail()))
|
||||
.getAsFile(zip, f -> {
|
||||
progress.dismiss();
|
||||
Intent intent = new Intent(activity, ClassMap.get(FlashActivity.class))
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.setData(Uri.fromFile(f))
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
||||
activity.startActivity(intent);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.topjohnwu.magisk.view.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import android.widget.Toast
|
||||
|
||||
import com.topjohnwu.magisk.ClassMap
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.ProgressNotification
|
||||
import com.topjohnwu.net.Networking
|
||||
import com.topjohnwu.superuser.Shell
|
||||
|
||||
import java.io.File
|
||||
|
||||
class UninstallDialog(activity: Activity) : CustomAlertDialog(activity) {
|
||||
|
||||
init {
|
||||
setTitle(R.string.uninstall_magisk_title)
|
||||
setMessage(R.string.uninstall_magisk_msg)
|
||||
setNeutralButton(R.string.restore_img) { _, _ ->
|
||||
val dialog = ProgressDialog.show(activity,
|
||||
activity.getString(R.string.restore_img),
|
||||
activity.getString(R.string.restore_img_msg))
|
||||
Shell.su("restore_imgs").submit { result ->
|
||||
dialog.cancel()
|
||||
if (result.isSuccess) {
|
||||
Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT)
|
||||
} else {
|
||||
Utils.toast(R.string.restore_fail, Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!TextUtils.isEmpty(Info.uninstallerLink)) {
|
||||
setPositiveButton(R.string.complete_uninstall) { d, i ->
|
||||
val zip = File(activity.filesDir, "uninstaller.zip")
|
||||
val progress = ProgressNotification(zip.name)
|
||||
Networking.get(Info.uninstallerLink)
|
||||
.setDownloadProgressListener(progress)
|
||||
.setErrorHandler { _, _ -> progress.dlFail() }
|
||||
.getAsFile(zip) { f ->
|
||||
progress.dismiss()
|
||||
val intent = Intent(activity, ClassMap[FlashActivity::class.java])
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.setData(Uri.fromFile(f))
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user