From 6907651756b43883cece3ca40fa1b2bfb32f01e3 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Tue, 24 Mar 2020 18:07:20 +0100 Subject: [PATCH] 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. --- app/src/main/AndroidManifest.xml | 3 - app/src/main/java/a/stubs.kt | 3 - .../java/com/topjohnwu/magisk/core/Const.kt | 1 + .../java/com/topjohnwu/magisk/core/Hacks.kt | 2 - .../magisk/core/download/DownloadService.kt | 28 ++-- .../magisk/core/download/RemoteFileService.kt | 5 +- .../topjohnwu/magisk/di/ViewModelsModule.kt | 5 +- .../magisk/legacy/flash/FlashActivity.kt | 110 -------------- .../magisk/legacy/flash/FlashViewModel.kt | 117 --------------- .../events/InstallExternalModuleEvent.kt | 16 +- .../magisk/ui/base/BaseUIActivity.kt | 19 +++ .../magisk/ui/flash/FlashFragment.kt | 136 ++++++++++++++++- .../magisk/ui/flash/FlashViewModel.kt | 138 +++++++++++++++++- .../magisk/ui/install/InstallViewModel.kt | 2 +- .../magisk/ui/module/ModuleFragment.kt | 4 +- app/src/main/res/layout/activity_flash.xml | 102 ------------- .../main/res/layout/fragment_flash_md2.xml | 75 +++++++++- app/src/main/res/navigation/main.xml | 37 +++++ app/src/main/res/values/strings.xml | 1 + 19 files changed, 435 insertions(+), 369 deletions(-) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashActivity.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashViewModel.kt delete mode 100644 app/src/main/res/layout/activity_flash.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 033ceef1f..b02053369 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,9 +29,6 @@ - - - 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 } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/RemoteFileService.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/RemoteFileService.kt index 18a1a475b..222d99783 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/RemoteFileService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/RemoteFileService.kt @@ -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(ARG_URL)?.let { start(it) } + intent?.getParcelableExtra(ARG_URL)?.let { + update(it.hashCode()) + start(it) + } return START_REDELIVER_INTENT } diff --git a/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt index 527aceab6..2b658f440 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt @@ -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()) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashActivity.kt deleted file mode 100644 index 9d731e08b..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashActivity.kt +++ /dev/null @@ -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() { - - 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() - .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)) - - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashViewModel.kt deleted file mode 100644 index 67bff9ed4..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/legacy/flash/FlashViewModel.kt +++ /dev/null @@ -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() - val items = diffListOf() - val itemBinding = itemBindingOf() - - private val outItems = ObservableArrayList() - private val logItems = Collections.synchronizedList(mutableListOf()) - - 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() - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt index e4a955ecf..ba452a276 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt @@ -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,13 +20,14 @@ 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() - 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 } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt index 6cc96d67e..96a3e8cf8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt @@ -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() + + fun postDirections(navDirections: NavDirections) = + directionsDispatcher.postValue(navDirections) + + } + } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt index 009cc6e5f..4871ebd8f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt @@ -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() { override val layoutRes = R.layout.fragment_flash_md2 - override val viewModel by viewModel() + override val viewModel by viewModel { + 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) + .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) } + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index 4f1b716ed..3747c5c36 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -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() + val items = diffListOf() + val itemBinding = itemBindingOf() + + private val outItems = ObservableArrayList() + private val logItems = + Collections.synchronizedList(mutableListOf()) + + 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() + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt index 2d8fa4110..5dfd530ed 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt @@ -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 { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt index e172ba9c3..3f194608d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt @@ -42,7 +42,9 @@ class ModuleFragment : BaseUIFragment 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() { diff --git a/app/src/main/res/layout/activity_flash.xml b/app/src/main/res/layout/activity_flash.xml deleted file mode 100644 index 17a46775e..000000000 --- a/app/src/main/res/layout/activity_flash.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_flash_md2.xml b/app/src/main/res/layout/fragment_flash_md2.xml index 55900f5c7..aa2d61da0 100644 --- a/app/src/main/res/layout/fragment_flash_md2.xml +++ b/app/src/main/res/layout/fragment_flash_md2.xml @@ -1,5 +1,7 @@ - + @@ -9,15 +11,74 @@ - + android:layout_height="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"> - + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/main.xml b/app/src/main/res/navigation/main.xml index ca0254d91..f75adda2e 100644 --- a/app/src/main/res/navigation/main.xml +++ b/app/src/main/res/navigation/main.xml @@ -35,6 +35,33 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 715a80d79..024001f81 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,6 +61,7 @@ Select and Patch a File Select a raw image (*.img) or an ODIN tarfile (*.tar) Rebooting in 5 seconds… + Installation Superuser Request