mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-30 21:45:27 +00:00
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
|
package com.topjohnwu.magisk.ui.base
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.collection.SparseArrayCompat
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@ -17,6 +19,7 @@ import com.karumi.dexter.listener.PermissionRequest
|
|||||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||||
import com.ncapdevi.fragnav.FragNavController
|
import com.ncapdevi.fragnav.FragNavController
|
||||||
import com.ncapdevi.fragnav.FragNavTransactionOptions
|
import com.ncapdevi.fragnav.FragNavTransactionOptions
|
||||||
|
import com.skoumal.teanity.view.TeanityActivity
|
||||||
import com.skoumal.teanity.viewevents.ViewEvent
|
import com.skoumal.teanity.viewevents.ViewEvent
|
||||||
import com.topjohnwu.magisk.Config
|
import com.topjohnwu.magisk.Config
|
||||||
import com.topjohnwu.magisk.model.events.BackPressEvent
|
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.model.permissions.PermissionRequestBuilder
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager
|
import com.topjohnwu.magisk.utils.LocaleManager
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
|
import com.topjohnwu.magisk.utils.set
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
typealias RequestCallback = MagiskActivity<*, *>.(Int, Intent?) -> Unit
|
||||||
|
|
||||||
abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBinding> :
|
abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBinding> :
|
||||||
MagiskLeanbackActivity<ViewModel, Binding>(), FragNavController.RootFragmentListener,
|
TeanityActivity<ViewModel, Binding>(), FragNavController.RootFragmentListener,
|
||||||
Navigator, FragNavController.TransactionListener {
|
Navigator, FragNavController.TransactionListener {
|
||||||
|
|
||||||
override val numberOfRootFragments: Int get() = baseFragments.size
|
override val numberOfRootFragments: Int get() = baseFragments.size
|
||||||
override val baseFragments: List<KClass<out Fragment>> = listOf()
|
override val baseFragments: List<KClass<out Fragment>> = listOf()
|
||||||
|
private val resultCallbacks = SparseArrayCompat<RequestCallback>()
|
||||||
|
|
||||||
|
|
||||||
protected open val defaultPosition: Int = 0
|
protected open val defaultPosition: Int = 0
|
||||||
|
|
||||||
@ -188,19 +195,23 @@ abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBin
|
|||||||
Dexter.withActivity(this)
|
Dexter.withActivity(this)
|
||||||
.withPermissions(*permissions)
|
.withPermissions(*permissions)
|
||||||
.withListener(object : MultiplePermissionsListener {
|
.withListener(object : MultiplePermissionsListener {
|
||||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) =
|
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
|
||||||
if (report?.areAllPermissionsGranted() == true) {
|
if (report.areAllPermissionsGranted()) {
|
||||||
request.onSuccess()
|
request.onSuccess()
|
||||||
} else {
|
} else {
|
||||||
request.onFailure()
|
request.onFailure()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPermissionRationaleShouldBeShown(
|
override fun onPermissionRationaleShouldBeShown(
|
||||||
permissions: MutableList<PermissionRequest>?,
|
permissions: MutableList<PermissionRequest>,
|
||||||
token: PermissionToken?
|
token: PermissionToken
|
||||||
) = request.onShowRationale(permissions.orEmpty().map { it.name })
|
) = token.continuePermissionRequest()
|
||||||
})
|
}).check()
|
||||||
.check()
|
}
|
||||||
|
|
||||||
|
fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) {
|
||||||
|
withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) =
|
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() {
|
private fun selectFile() {
|
||||||
magiskActivity.runWithExternalRW {
|
magiskActivity.withExternalRW {
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
onSuccess {
|
||||||
intent.type = "application/zip"
|
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
startActivityForResult(intent, Const.ID.FETCH_ZIP)
|
intent.type = "application/zip"
|
||||||
|
startActivityForResult(intent, Const.ID.FETCH_ZIP)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,20 +74,22 @@ class ReposFragment : MagiskFragment<ModuleViewModel, FragmentReposBinding>(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openChangelog(item: Repo) {
|
private fun openChangelog(item: Repo) {
|
||||||
MarkDownWindow.show(context, null, item.detailUrl)
|
MarkDownWindow.show(requireActivity(), null, item.detailUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installModule(item: Repo) {
|
private fun installModule(item: Repo) {
|
||||||
val context = magiskActivity
|
val context = magiskActivity
|
||||||
|
|
||||||
fun download(install: Boolean) {
|
fun download(install: Boolean) {
|
||||||
context.runWithExternalRW {
|
context.withExternalRW {
|
||||||
val intent = Intent(activity, ClassMap[DownloadModuleService::class.java])
|
onSuccess {
|
||||||
.putExtra("repo", item).putExtra("install", install)
|
val intent = Intent(activity, ClassMap[DownloadModuleService::class.java])
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
.putExtra("repo", item).putExtra("install", install)
|
||||||
context.startForegroundService(intent) //hmm, service starts itself in foreground, this seems unnecessary
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
} else {
|
context.startForegroundService(intent)
|
||||||
context.startService(intent)
|
} else {
|
||||||
|
context.startService(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.utils
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import androidx.collection.SparseArrayCompat
|
||||||
import androidx.databinding.ObservableList
|
import androidx.databinding.ObservableList
|
||||||
import com.skoumal.teanity.extensions.subscribeK
|
import com.skoumal.teanity.extensions.subscribeK
|
||||||
import com.skoumal.teanity.util.DiffObservableList
|
import com.skoumal.teanity.util.DiffObservableList
|
||||||
@ -76,4 +77,8 @@ fun <T1> ObservableList<T1>.copyNewInputInto(
|
|||||||
val addedValues = sender?.slice(positionStart until positionEnd).orEmpty()
|
val addedValues = sender?.slice(positionStart until positionEnd).orEmpty()
|
||||||
target.addAll(addedValues)
|
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