Updated flash screen so it's a fragment
The FlashActivity has been removed and all of it's functionality has been transferred to the FlashFragment. The FlashFragment needs to be however launched in a different way than the activity using the MainActivity's stub and so seemingly massive changes had to be made. Notably the RemoteFileService didn't seem to be calling Service.startForeground(), which has been crashing the application due to the system requirements, so that's been fixed.
This commit is contained in:
parent
fc2d0246e6
commit
6907651756
@ -29,9 +29,6 @@
|
||||
<!-- Main -->
|
||||
<activity android:name="a.b" />
|
||||
|
||||
<!-- Flashing -->
|
||||
<activity android:name="a.f" />
|
||||
|
||||
<!-- Superuser -->
|
||||
<activity
|
||||
android:name="a.m"
|
||||
|
@ -4,7 +4,6 @@ import com.topjohnwu.magisk.core.App
|
||||
import com.topjohnwu.magisk.core.GeneralReceiver
|
||||
import com.topjohnwu.magisk.core.SplashActivity
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.legacy.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.legacy.surequest.SuRequestActivity
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
|
||||
@ -17,8 +16,6 @@ class e : App {
|
||||
constructor(o: Any) : super(o)
|
||||
}
|
||||
|
||||
class f : FlashActivity()
|
||||
|
||||
class h : GeneralReceiver()
|
||||
|
||||
class j : DownloadService()
|
||||
|
@ -64,6 +64,7 @@ object Const {
|
||||
const val OPEN_SECTION = "section"
|
||||
const val OPEN_SETTINGS = "settings"
|
||||
const val INTENT_SET_APP = "app_json"
|
||||
const val FLASH_INSTALLER = "installer"
|
||||
const val FLASH_ACTION = "action"
|
||||
const val FLASH_DATA = "additional_data"
|
||||
const val DISMISS_ID = "dismiss_id"
|
||||
|
@ -21,7 +21,6 @@ import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
||||
import com.topjohnwu.magisk.extensions.forceGetDeclaredField
|
||||
import com.topjohnwu.magisk.legacy.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.legacy.surequest.SuRequestActivity
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
|
||||
@ -146,7 +145,6 @@ object ClassMap {
|
||||
App::class.java to a.e::class.java,
|
||||
MainActivity::class.java to a.b::class.java,
|
||||
SplashActivity::class.java to a.c::class.java,
|
||||
FlashActivity::class.java to a.f::class.java,
|
||||
GeneralReceiver::class.java to a.h::class.java,
|
||||
DownloadService::class.java to a.j::class.java,
|
||||
SuRequestActivity::class.java to a.m::class.java,
|
||||
|
@ -14,11 +14,11 @@ import com.topjohnwu.magisk.extensions.chooser
|
||||
import com.topjohnwu.magisk.extensions.exists
|
||||
import com.topjohnwu.magisk.extensions.provide
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.legacy.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.model.entity.internal.Configuration.*
|
||||
import com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import io.reactivex.Completable
|
||||
import org.koin.core.get
|
||||
@ -45,10 +45,12 @@ open class DownloadService : RemoteFileService() {
|
||||
subject: Magisk,
|
||||
id: Int
|
||||
) = when (val conf = subject.configuration) {
|
||||
Uninstall -> FlashActivity.uninstall(this, subject.file, id)
|
||||
EnvFix -> { remove(id); EnvFixTask(subject.file).exec() }
|
||||
is Patch -> FlashActivity.patch(this, subject.file, conf.fileUri, id)
|
||||
is Flash -> FlashActivity.flash(this, subject.file, conf is Secondary, id)
|
||||
Uninstall -> FlashFragment.uninstall(subject.file, id)
|
||||
EnvFix -> {
|
||||
remove(id); EnvFixTask(subject.file).exec()
|
||||
}
|
||||
is Patch -> FlashFragment.patch(subject.file, conf.fileUri, id)
|
||||
is Flash -> FlashFragment.flash(subject.file, conf is Secondary, id)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
@ -56,7 +58,7 @@ open class DownloadService : RemoteFileService() {
|
||||
subject: Module,
|
||||
id: Int
|
||||
) = when (subject.configuration) {
|
||||
is Flash -> FlashActivity.install(this, subject.file, id)
|
||||
is Flash -> FlashFragment.install(subject.file, id)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
@ -94,9 +96,15 @@ open class DownloadService : RemoteFileService() {
|
||||
.takeIf { it.exists(get()) }
|
||||
?.let { addAction(0, R.string.download_open_self, it.chooser()) }
|
||||
}
|
||||
Uninstall -> setContentIntent(FlashActivity.uninstallIntent(context, subject.file))
|
||||
is Flash -> setContentIntent(FlashActivity.flashIntent(context, subject.file, conf is Secondary))
|
||||
is Patch -> setContentIntent(FlashActivity.patchIntent(context, subject.file, conf.fileUri))
|
||||
Uninstall -> setContentIntent(FlashFragment.uninstallIntent(context, subject.file))
|
||||
is Flash -> setContentIntent(
|
||||
FlashFragment.flashIntent(
|
||||
context,
|
||||
subject.file,
|
||||
conf is Secondary
|
||||
)
|
||||
)
|
||||
is Patch -> setContentIntent(FlashFragment.patchIntent(context, subject.file, conf.fileUri))
|
||||
else -> this
|
||||
}
|
||||
|
||||
@ -110,7 +118,7 @@ open class DownloadService : RemoteFileService() {
|
||||
.takeIf { it.exists(get()) }
|
||||
?.let { addAction(0, R.string.download_open_self, it.chooser()) }
|
||||
}
|
||||
is Flash -> setContentIntent(FlashActivity.installIntent(context, subject.file))
|
||||
is Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
|
||||
else -> this
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,10 @@ abstract class RemoteFileService : NotificationService() {
|
||||
val service: GithubRawServices by inject()
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
intent?.getParcelableExtra<DownloadSubject>(ARG_URL)?.let { start(it) }
|
||||
intent?.getParcelableExtra<DownloadSubject>(ARG_URL)?.let {
|
||||
update(it.hashCode())
|
||||
start(it)
|
||||
}
|
||||
return START_REDELIVER_INTENT
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import com.topjohnwu.magisk.legacy.flash.FlashViewModel
|
||||
import com.topjohnwu.magisk.legacy.surequest.SuRequestViewModel
|
||||
import com.topjohnwu.magisk.ui.MainViewModel
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs
|
||||
import com.topjohnwu.magisk.ui.flash.FlashViewModel
|
||||
import com.topjohnwu.magisk.ui.hide.HideViewModel
|
||||
import com.topjohnwu.magisk.ui.home.HomeViewModel
|
||||
import com.topjohnwu.magisk.ui.install.InstallViewModel
|
||||
@ -30,6 +31,6 @@ val viewModelModules = module {
|
||||
viewModel { MainViewModel() }
|
||||
|
||||
// Legacy
|
||||
viewModel { FlashViewModel(get()) }
|
||||
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args, get()) }
|
||||
viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) }
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
package com.topjohnwu.magisk.legacy.flash
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.core.net.toUri
|
||||
import androidx.navigation.NavController
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.intent
|
||||
import com.topjohnwu.magisk.core.view.Notifications
|
||||
import com.topjohnwu.magisk.databinding.ActivityFlashBinding
|
||||
import com.topjohnwu.magisk.extensions.snackbar
|
||||
import com.topjohnwu.magisk.model.events.PermissionEvent
|
||||
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.model.events.ViewEvent
|
||||
import com.topjohnwu.magisk.ui.base.BaseUIActivity
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import java.io.File
|
||||
|
||||
open class FlashActivity : BaseUIActivity<FlashViewModel, ActivityFlashBinding>() {
|
||||
|
||||
override val layoutRes: Int = R.layout.activity_flash
|
||||
override val viewModel: FlashViewModel by viewModel()
|
||||
|
||||
override val navigation: NavController? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||
super.onCreate(savedInstanceState)
|
||||
val id = intent.getIntExtra(Const.Key.DISMISS_ID, -1)
|
||||
if (id != -1)
|
||||
Notifications.mgr.cancel(id)
|
||||
viewModel.startFlashing(intent)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (viewModel.loading) return
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) {
|
||||
super.onEventDispatched(event)
|
||||
when (event) {
|
||||
is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f)
|
||||
is PermissionEvent -> withPermissions(*event.permissions.toTypedArray()) {
|
||||
onSuccess { event.callback.onNext(true) }
|
||||
onFailure {
|
||||
event.callback.onNext(false)
|
||||
event.callback.onError(SecurityException("User refused permissions"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun intent(context: Context) = context.intent<FlashActivity>()
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
private fun intent(context: Context, file: File) = intent(context).setData(file.toUri())
|
||||
|
||||
private fun flashType(isSecondSlot: Boolean) =
|
||||
if (isSecondSlot) Const.Value.FLASH_INACTIVE_SLOT else Const.Value.FLASH_MAGISK
|
||||
|
||||
/* Flashing is understood as installing / flashing magisk itself */
|
||||
|
||||
fun flashIntent(context: Context, file: File, isSecondSlot: Boolean, id : Int = -1)
|
||||
= intent(context, file)
|
||||
.putExtra(Const.Key.FLASH_ACTION, flashType(isSecondSlot))
|
||||
.putExtra(Const.Key.DISMISS_ID, id)
|
||||
|
||||
fun flash(context: Context, file: File, isSecondSlot: Boolean, id: Int) =
|
||||
context.startActivity(flashIntent(context, file, isSecondSlot, id))
|
||||
|
||||
/* Patching is understood as injecting img files with magisk */
|
||||
|
||||
fun patchIntent(context: Context, file: File, uri: Uri, id : Int = -1)
|
||||
= intent(context, file)
|
||||
.putExtra(Const.Key.FLASH_DATA, uri)
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_FILE)
|
||||
.putExtra(Const.Key.DISMISS_ID, id)
|
||||
|
||||
fun patch(context: Context, file: File, uri: Uri, id: Int) =
|
||||
context.startActivity(patchIntent(context, file, uri, id))
|
||||
|
||||
/* Uninstalling is understood as removing magisk entirely */
|
||||
|
||||
fun uninstallIntent(context: Context, file: File, id : Int = -1)
|
||||
= intent(context, file)
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL)
|
||||
.putExtra(Const.Key.DISMISS_ID, id)
|
||||
|
||||
fun uninstall(context: Context, file: File, id: Int) =
|
||||
context.startActivity(uninstallIntent(context, file, id))
|
||||
|
||||
/* Installing is understood as flashing modules / zips */
|
||||
|
||||
fun installIntent(context: Context, file: File, id : Int = -1)
|
||||
= intent(context, file)
|
||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP)
|
||||
.putExtra(Const.Key.DISMISS_ID, id)
|
||||
|
||||
fun install(context: Context, file: File, id: Int) =
|
||||
context.startActivity(installIntent(context, file, id))
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
package com.topjohnwu.magisk.legacy.flash
|
||||
|
||||
import android.Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.view.MenuItem
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.databinding.ObservableArrayList
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.tasks.FlashResultListener
|
||||
import com.topjohnwu.magisk.core.tasks.Flashing
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.extensions.*
|
||||
import com.topjohnwu.magisk.model.binding.BindingAdapter
|
||||
import com.topjohnwu.magisk.model.entity.recycler.ConsoleItem
|
||||
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.ui.base.BaseViewModel
|
||||
import com.topjohnwu.magisk.ui.base.diffListOf
|
||||
import com.topjohnwu.magisk.ui.base.itemBindingOf
|
||||
import com.topjohnwu.magisk.utils.KObservableField
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class FlashViewModel(
|
||||
private val resources: Resources
|
||||
) : BaseViewModel(), FlashResultListener {
|
||||
|
||||
val canShowReboot = Shell.rootAccess()
|
||||
val showRestartTitle = KObservableField(false)
|
||||
|
||||
val behaviorText = KObservableField(resources.getString(R.string.flashing))
|
||||
|
||||
val adapter = BindingAdapter<ConsoleItem>()
|
||||
val items = diffListOf<ConsoleItem>()
|
||||
val itemBinding = itemBindingOf<ConsoleItem>()
|
||||
|
||||
private val outItems = ObservableArrayList<String>()
|
||||
private val logItems = Collections.synchronizedList(mutableListOf<String>())
|
||||
|
||||
init {
|
||||
outItems.sendUpdatesTo(items) { it.map { ConsoleItem(it) } }
|
||||
outItems.copyNewInputInto(logItems)
|
||||
}
|
||||
|
||||
fun startFlashing(intent: Intent) {
|
||||
val installer = intent.data ?: return
|
||||
val uri: Uri? = intent.getParcelableExtra(Const.Key.FLASH_DATA)
|
||||
val action = intent.getStringExtra(Const.Key.FLASH_ACTION) ?: return
|
||||
|
||||
when (action) {
|
||||
Const.Value.FLASH_ZIP -> Flashing
|
||||
.Install(installer, outItems, logItems, this)
|
||||
.exec()
|
||||
Const.Value.UNINSTALL -> Flashing
|
||||
.Uninstall(installer, outItems, logItems, this)
|
||||
.exec()
|
||||
Const.Value.FLASH_MAGISK -> MagiskInstaller
|
||||
.Direct(installer, outItems, logItems, this)
|
||||
.exec()
|
||||
Const.Value.FLASH_INACTIVE_SLOT -> MagiskInstaller
|
||||
.SecondSlot(installer, outItems, logItems, this)
|
||||
.exec()
|
||||
Const.Value.PATCH_FILE -> MagiskInstaller
|
||||
.Patch(installer, uri ?: return, outItems, logItems, this)
|
||||
.exec()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResult(success: Boolean) {
|
||||
state = if (success) State.LOADED else State.LOADING_FAILED
|
||||
behaviorText.value = when {
|
||||
success -> resources.getString(R.string.done)
|
||||
else -> resources.getString(R.string.failure)
|
||||
}
|
||||
|
||||
if (success) {
|
||||
Handler().postDelayed(500) {
|
||||
showRestartTitle.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onMenuItemClicked(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_save -> savePressed()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun savePressed() = withPermissions(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE)
|
||||
.map { now }
|
||||
.map { it.toTime(timeFormatStandard) }
|
||||
.map { Const.MAGISK_INSTALL_LOG_FILENAME.format(it) }
|
||||
.map { File(Config.downloadDirectory, it) }
|
||||
.map { file ->
|
||||
file.bufferedWriter().use { writer ->
|
||||
logItems.forEach {
|
||||
writer.write(it)
|
||||
writer.newLine()
|
||||
}
|
||||
}
|
||||
file.path
|
||||
}
|
||||
.subscribeK { SnackbarEvent(it).publish() }
|
||||
.add()
|
||||
|
||||
fun restartPressed() = reboot()
|
||||
|
||||
fun backPressed() = back()
|
||||
|
||||
}
|
@ -2,13 +2,12 @@ package com.topjohnwu.magisk.model.events
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.navigation.NavDirections
|
||||
import com.topjohnwu.magisk.MainDirections
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.core.intent
|
||||
import com.topjohnwu.magisk.legacy.flash.FlashActivity
|
||||
|
||||
class InstallExternalModuleEvent : ViewEvent(), ActivityExecutor {
|
||||
|
||||
@ -21,14 +20,15 @@ class InstallExternalModuleEvent : ViewEvent(), ActivityExecutor {
|
||||
|
||||
companion object {
|
||||
|
||||
fun onActivityResult(context: Context, requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): NavDirections? {
|
||||
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
|
||||
// Get the URI of the selected file
|
||||
val intent = context.intent<FlashActivity>()
|
||||
intent.setData(data.data).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP)
|
||||
context.startActivity(intent)
|
||||
val data = data.data
|
||||
if (data != null) {
|
||||
return MainDirections.actionFlashFragment(data, Const.Key.FLASH_ACTION)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ import androidx.core.graphics.Insets
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.OnRebindCallback
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.findNavController
|
||||
import com.topjohnwu.magisk.BR
|
||||
@ -71,6 +73,14 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
|
||||
})
|
||||
|
||||
delegate.onCreate()
|
||||
|
||||
directionsDispatcher.observe(this, Observer {
|
||||
it?.navigate()
|
||||
// we don't want the directions to be re-dispatched, so we preemptively set them to null
|
||||
if (it != null) {
|
||||
directionsDispatcher.value = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -101,4 +111,13 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
|
||||
navigation?.navigate(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val directionsDispatcher = MutableLiveData<NavDirections?>()
|
||||
|
||||
fun postDirections(navDirections: NavDirections) =
|
||||
directionsDispatcher.postValue(navDirections)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,147 @@
|
||||
package com.topjohnwu.magisk.ui.flash
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.net.toUri
|
||||
import androidx.navigation.NavDeepLinkBuilder
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.ClassMap
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.databinding.FragmentFlashMd2Binding
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.base.BaseUIActivity
|
||||
import com.topjohnwu.magisk.ui.base.BaseUIFragment
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import java.io.File
|
||||
import com.topjohnwu.magisk.MainDirections.Companion.actionFlashFragment as toFlash
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs as args
|
||||
|
||||
class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>() {
|
||||
|
||||
override val layoutRes = R.layout.fragment_flash_md2
|
||||
override val viewModel by viewModel<FlashViewModel>()
|
||||
override val viewModel by viewModel<FlashViewModel> {
|
||||
parametersOf(args.fromBundle(requireArguments()))
|
||||
}
|
||||
|
||||
private var defaultOrientation = -1
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setHasOptionsMenu(true)
|
||||
activity.setTitle(R.string.flash_screen_title)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_flash, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return viewModel.onMenuItemClicked(item)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
defaultOrientation = activity.requestedOrientation
|
||||
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
override fun onDestroyView() {
|
||||
if (defaultOrientation != -1) {
|
||||
activity.requestedOrientation = defaultOrientation
|
||||
}
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
if (viewModel.loading) return true
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onPreBind(binding: FragmentFlashMd2Binding) = Unit
|
||||
|
||||
companion object {
|
||||
|
||||
private fun createIntent(context: Context, args: args): PendingIntent {
|
||||
return NavDeepLinkBuilder(context)
|
||||
.setGraph(R.navigation.main)
|
||||
.setComponentName(ClassMap[MainActivity::class.java] as Class<out Activity>)
|
||||
.setDestination(R.id.flashFragment)
|
||||
.setArguments(args.toBundle())
|
||||
.createPendingIntent()
|
||||
}
|
||||
|
||||
private fun flashType(isSecondSlot: Boolean) =
|
||||
if (isSecondSlot) Const.Value.FLASH_INACTIVE_SLOT else Const.Value.FLASH_MAGISK
|
||||
|
||||
/* Flashing is understood as installing / flashing magisk itself */
|
||||
|
||||
fun flashIntent(context: Context, file: File, isSecondSlot: Boolean, id: Int = -1) = args(
|
||||
installer = file.toUri(),
|
||||
action = flashType(isSecondSlot),
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun flash(file: File, isSecondSlot: Boolean, id: Int) = toFlash(
|
||||
installer = file.toUri(),
|
||||
action = flashType(isSecondSlot),
|
||||
dismissId = id
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
|
||||
/* Patching is understood as injecting img files with magisk */
|
||||
|
||||
fun patchIntent(context: Context, file: File, uri: Uri, id: Int = -1) = args(
|
||||
installer = file.toUri(),
|
||||
action = Const.Value.PATCH_FILE,
|
||||
additionalData = uri,
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun patch(file: File, uri: Uri, id: Int) = toFlash(
|
||||
installer = file.toUri(),
|
||||
action = Const.Value.PATCH_FILE,
|
||||
additionalData = uri,
|
||||
dismissId = id
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
|
||||
/* Uninstalling is understood as removing magisk entirely */
|
||||
|
||||
fun uninstallIntent(context: Context, file: File, id: Int = -1) = args(
|
||||
installer = file.toUri(),
|
||||
action = Const.Value.UNINSTALL,
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun uninstall(file: File, id: Int) = toFlash(
|
||||
installer = file.toUri(),
|
||||
action = Const.Value.UNINSTALL,
|
||||
dismissId = id
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
|
||||
/* Installing is understood as flashing modules / zips */
|
||||
|
||||
fun installIntent(context: Context, file: File, id: Int = -1) = args(
|
||||
installer = file.toUri(),
|
||||
action = Const.Value.FLASH_ZIP,
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun install(file: File, id: Int) = toFlash(
|
||||
installer = file.toUri(),
|
||||
action = Const.Value.FLASH_ZIP,
|
||||
dismissId = id
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,141 @@
|
||||
package com.topjohnwu.magisk.ui.flash
|
||||
|
||||
import android.Manifest
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.view.MenuItem
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.databinding.ObservableArrayList
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.tasks.FlashResultListener
|
||||
import com.topjohnwu.magisk.core.tasks.Flashing
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.core.view.Notifications
|
||||
import com.topjohnwu.magisk.extensions.*
|
||||
import com.topjohnwu.magisk.model.binding.BindingAdapter
|
||||
import com.topjohnwu.magisk.model.entity.recycler.ConsoleItem
|
||||
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.ui.base.BaseViewModel
|
||||
import com.topjohnwu.magisk.ui.base.diffListOf
|
||||
import com.topjohnwu.magisk.ui.base.itemBindingOf
|
||||
import com.topjohnwu.magisk.utils.KObservableField
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class FlashViewModel : BaseViewModel()
|
||||
class FlashViewModel(
|
||||
private val args: FlashFragmentArgs,
|
||||
private val resources: Resources
|
||||
) : BaseViewModel(),
|
||||
FlashResultListener {
|
||||
|
||||
val canShowReboot = Shell.rootAccess()
|
||||
val showRestartTitle = KObservableField(false)
|
||||
|
||||
val behaviorText = KObservableField(resources.getString(R.string.flashing))
|
||||
|
||||
val adapter = BindingAdapter<ConsoleItem>()
|
||||
val items = diffListOf<ConsoleItem>()
|
||||
val itemBinding = itemBindingOf<ConsoleItem>()
|
||||
|
||||
private val outItems = ObservableArrayList<String>()
|
||||
private val logItems =
|
||||
Collections.synchronizedList(mutableListOf<String>())
|
||||
|
||||
init {
|
||||
outItems.sendUpdatesTo(items) { it.map { ConsoleItem(it) } }
|
||||
outItems.copyNewInputInto(logItems)
|
||||
|
||||
args.dismissId.takeIf { it != -1 }?.also {
|
||||
Notifications.mgr.cancel(it)
|
||||
}
|
||||
val (installer, action, uri) = args
|
||||
startFlashing(installer, uri, action)
|
||||
}
|
||||
|
||||
private fun startFlashing(installer: Uri, uri: Uri?, action: String) {
|
||||
when (action) {
|
||||
Const.Value.FLASH_ZIP -> Flashing.Install(
|
||||
installer,
|
||||
outItems,
|
||||
logItems,
|
||||
this
|
||||
).exec()
|
||||
Const.Value.UNINSTALL -> Flashing.Uninstall(
|
||||
installer,
|
||||
outItems,
|
||||
logItems,
|
||||
this
|
||||
).exec()
|
||||
Const.Value.FLASH_MAGISK -> MagiskInstaller.Direct(
|
||||
installer,
|
||||
outItems,
|
||||
logItems,
|
||||
this
|
||||
).exec()
|
||||
Const.Value.FLASH_INACTIVE_SLOT -> MagiskInstaller.SecondSlot(
|
||||
installer,
|
||||
outItems,
|
||||
logItems,
|
||||
this
|
||||
).exec()
|
||||
Const.Value.PATCH_FILE -> MagiskInstaller.Patch(
|
||||
installer,
|
||||
uri ?: return,
|
||||
outItems,
|
||||
logItems,
|
||||
this
|
||||
).exec()
|
||||
else -> backPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResult(success: Boolean) {
|
||||
state = if (success) State.LOADED else State.LOADING_FAILED
|
||||
behaviorText.value = when {
|
||||
success -> resources.getString(R.string.done)
|
||||
else -> resources.getString(R.string.failure)
|
||||
}
|
||||
|
||||
if (success) {
|
||||
Handler().postDelayed(500) {
|
||||
showRestartTitle.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onMenuItemClicked(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_save -> savePressed()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun savePressed() = withPermissions(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
.map { now }
|
||||
.map { it.toTime(timeFormatStandard) }
|
||||
.map { Const.MAGISK_INSTALL_LOG_FILENAME.format(it) }
|
||||
.map { File(Config.downloadDirectory, it) }
|
||||
.map { file ->
|
||||
file.bufferedWriter().use { writer ->
|
||||
logItems.forEach {
|
||||
writer.write(it)
|
||||
writer.newLine()
|
||||
}
|
||||
}
|
||||
file.path
|
||||
}
|
||||
.subscribeK { SnackbarEvent(it).publish() }
|
||||
.add()
|
||||
|
||||
fun restartPressed() = reboot()
|
||||
|
||||
fun backPressed() = back()
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ class InstallViewModel : BaseViewModel(State.LOADED) {
|
||||
this.progress.value = progress.times(100).roundToInt()
|
||||
if (this.progress.value >= 100) {
|
||||
// this might cause issues if the flash activity launches on top of this sooner
|
||||
back()
|
||||
// back()
|
||||
}
|
||||
}
|
||||
method.addOnPropertyChangedCallback {
|
||||
|
@ -42,7 +42,9 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
InstallExternalModuleEvent.onActivityResult(requireContext(), requestCode, resultCode, data)
|
||||
InstallExternalModuleEvent.onActivityResult(requestCode, resultCode, data)?.also {
|
||||
it.navigate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -1,102 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.topjohnwu.magisk.legacy.flash.FlashViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
style="@style/WidgetFoundation.Appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@{viewModel.insets.left}"
|
||||
android:paddingRight="@{viewModel.insets.right}">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
style="@style/WidgetFoundation.Toolbar"
|
||||
onMenuClick="@{(item) -> viewModel.onMenuItemClicked(item)}"
|
||||
onNavigationClick="@{() -> viewModel.backPressed()}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:paddingTop="@{viewModel.insets.top}"
|
||||
app:contentInsetLeft="0dp"
|
||||
app:contentInsetStart="0dp"
|
||||
app:menu="@menu/menu_flash"
|
||||
app:navigationIcon="@drawable/ic_back_md2">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
movieBehavior="@{viewModel.loading}"
|
||||
movieBehaviorText="@{viewModel.behaviorText}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="monospace"
|
||||
android:gravity="center"
|
||||
android:textColor="?colorOnSurface"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Flashing..." />
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
tools:layout_marginTop="@dimen/internal_action_bar_size">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/flash_content"
|
||||
adapter="@{viewModel.adapter}"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
scrollToLast="@{true}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@{viewModel.insets.left}"
|
||||
android:paddingRight="@{viewModel.insets.right}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/item_console_md2" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
gone="@{!viewModel.loaded || !viewModel.canShowReboot}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/l1"
|
||||
android:layout_marginBottom="@{(int) @dimen/l1 + viewModel.insets.bottom}"
|
||||
android:onClick="@{() -> viewModel.restartPressed()}"
|
||||
android:text="@string/reboot"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textStyle="bold"
|
||||
app:backgroundTint="?colorPrimary"
|
||||
app:icon="@drawable/ic_restart"
|
||||
app:tint="?colorOnPrimary" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
@ -1,5 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
@ -9,15 +11,74 @@
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
android:layout_marginTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
tools:layout_marginTop="@dimen/internal_action_bar_size">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/flash_content"
|
||||
adapter="@{viewModel.adapter}"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
scrollToLast="@{true}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@{viewModel.insets.left}"
|
||||
android:paddingRight="@{viewModel.insets.right}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/item_console_md2" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
gone="@{!viewModel.loaded || !viewModel.canShowReboot}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/l1"
|
||||
android:layout_marginBottom="@{(int) @dimen/l1 + viewModel.insets.bottom}"
|
||||
android:onClick="@{() -> viewModel.restartPressed()}"
|
||||
android:text="@string/reboot"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textStyle="bold"
|
||||
app:backgroundTint="?colorPrimary"
|
||||
app:icon="@drawable/ic_restart"
|
||||
app:iconTint="?colorOnPrimary" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card.Elevated"
|
||||
goneUnless="@{viewModel.loading}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:contentPadding="@dimen/l1">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
movieBehavior="@{viewModel.loading}"
|
||||
movieBehaviorText="@{viewModel.behaviorText}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:fontFamily="monospace"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textColor="?colorOnSurface"
|
||||
android:textStyle="bold"
|
||||
tools:text="Flashing..." />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
|
@ -35,6 +35,33 @@
|
||||
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/flashFragment"
|
||||
android:name="com.topjohnwu.magisk.ui.flash.FlashFragment"
|
||||
android:label="FlashFragment"
|
||||
tools:layout="@layout/fragment_flash_md2">
|
||||
|
||||
<argument
|
||||
android:name="installer"
|
||||
app:argType="android.net.Uri" />
|
||||
|
||||
<argument
|
||||
android:name="action"
|
||||
app:argType="string" />
|
||||
|
||||
<argument
|
||||
android:name="additional_data"
|
||||
android:defaultValue="@null"
|
||||
app:argType="android.net.Uri"
|
||||
app:nullable="true" />
|
||||
|
||||
<argument
|
||||
android:name="dismiss_id"
|
||||
android:defaultValue="-1"
|
||||
app:argType="integer" />
|
||||
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/installFragment"
|
||||
android:name="com.topjohnwu.magisk.ui.install.InstallFragment"
|
||||
@ -145,4 +172,14 @@
|
||||
app:popUpTo="@id/modulesFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_flashFragment"
|
||||
app:destination="@id/flashFragment"
|
||||
app:enterAnim="@anim/fragment_enter"
|
||||
app:exitAnim="@anim/fragment_exit"
|
||||
app:popEnterAnim="@anim/fragment_enter_pop"
|
||||
app:popExitAnim="@anim/fragment_exit_pop"
|
||||
app:popUpTo="@id/installFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
</navigation>
|
@ -61,6 +61,7 @@
|
||||
<string name="select_patch_file">Select and Patch a File</string>
|
||||
<string name="patch_file_msg">Select a raw image (*.img) or an ODIN tarfile (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Rebooting in 5 seconds…</string>
|
||||
<string name="flash_screen_title">Installation</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Superuser Request</string>
|
||||
|
Loading…
Reference in New Issue
Block a user