mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-06-21 22:32:04 -07:00
Migrate Install screen to Jetpack Compose with miuix
Replace data binding and @Bindable properties with StateFlow<UiState>. Implement InstallScreen composable with options card, method selection, and release notes. Remove fragment_install_md2.xml layout. Made-with: Cursor
This commit is contained in:
@@ -1,18 +1,58 @@
|
||||
package com.topjohnwu.magisk.ui.install
|
||||
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseFragment
|
||||
import com.topjohnwu.magisk.arch.viewModel
|
||||
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.topjohnwu.magisk.arch.ActivityExecutor
|
||||
import com.topjohnwu.magisk.arch.ContextExecutor
|
||||
import com.topjohnwu.magisk.arch.NavigationActivity
|
||||
import com.topjohnwu.magisk.arch.UIActivity
|
||||
import com.topjohnwu.magisk.arch.VMFactory
|
||||
import com.topjohnwu.magisk.arch.ViewEvent
|
||||
import com.topjohnwu.magisk.arch.ViewModelHolder
|
||||
import com.topjohnwu.magisk.ui.theme.MagiskTheme
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
class InstallFragment : BaseFragment<FragmentInstallMd2Binding>() {
|
||||
class InstallFragment : Fragment(), ViewModelHolder {
|
||||
|
||||
override val layoutRes = R.layout.fragment_install_md2
|
||||
override val viewModel by viewModel<InstallViewModel>()
|
||||
override val viewModel by lazy {
|
||||
ViewModelProvider(this, VMFactory)[InstallViewModel::class.java]
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
startObserveLiveData()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
requireActivity().setTitle(CoreR.string.install)
|
||||
(activity as? NavigationActivity<*>)?.setTitle(CoreR.string.install)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MagiskTheme {
|
||||
InstallScreen(viewModel = viewModel as InstallViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) {
|
||||
when (event) {
|
||||
is ContextExecutor -> event(requireContext())
|
||||
is ActivityExecutor -> (activity as? UIActivity<*>)?.let { event(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.topjohnwu.magisk.ui.install
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.Checkbox
|
||||
import top.yukonga.miuix.kmp.basic.Text
|
||||
import top.yukonga.miuix.kmp.basic.TextButton
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
@Composable
|
||||
fun InstallScreen(viewModel: InstallViewModel) {
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(top = 8.dp, bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
if (!viewModel.skipOptions) {
|
||||
OptionsCard(uiState = uiState, viewModel = viewModel)
|
||||
}
|
||||
|
||||
MethodCard(uiState = uiState, viewModel = viewModel)
|
||||
|
||||
if (uiState.notes.isNotEmpty()) {
|
||||
NotesCard(notes = uiState.notes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionsCard(uiState: InstallViewModel.UiState, viewModel: InstallViewModel) {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.install_options_title),
|
||||
style = MiuixTheme.textStyles.headline2,
|
||||
)
|
||||
if (uiState.step == 0) {
|
||||
TextButton(
|
||||
text = stringResource(CoreR.string.install_next),
|
||||
onClick = { viewModel.nextStep() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.step == 0) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
if (!Info.isSAR) {
|
||||
CheckboxRow(
|
||||
label = stringResource(CoreR.string.keep_dm_verity),
|
||||
checked = Config.keepVerity,
|
||||
onCheckedChange = { Config.keepVerity = it }
|
||||
)
|
||||
}
|
||||
if (Info.isFDE) {
|
||||
CheckboxRow(
|
||||
label = stringResource(CoreR.string.keep_force_encryption),
|
||||
checked = Config.keepEnc,
|
||||
onCheckedChange = { Config.keepEnc = it }
|
||||
)
|
||||
}
|
||||
if (!Info.ramdisk) {
|
||||
CheckboxRow(
|
||||
label = stringResource(CoreR.string.recovery_mode),
|
||||
checked = Config.recovery,
|
||||
onCheckedChange = { Config.recovery = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MethodCard(uiState: InstallViewModel.UiState, viewModel: InstallViewModel) {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.install_method_title),
|
||||
style = MiuixTheme.textStyles.headline2,
|
||||
)
|
||||
if (uiState.step == 1) {
|
||||
TextButton(
|
||||
text = stringResource(CoreR.string.install_start),
|
||||
onClick = { viewModel.install() },
|
||||
enabled = viewModel.canInstall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.step == 1) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
MethodRadioRow(
|
||||
label = stringResource(CoreR.string.select_patch_file),
|
||||
selected = uiState.method == InstallViewModel.Method.PATCH,
|
||||
onClick = { viewModel.selectMethod(InstallViewModel.Method.PATCH) }
|
||||
)
|
||||
if (viewModel.isRooted) {
|
||||
MethodRadioRow(
|
||||
label = stringResource(CoreR.string.direct_install),
|
||||
selected = uiState.method == InstallViewModel.Method.DIRECT,
|
||||
onClick = { viewModel.selectMethod(InstallViewModel.Method.DIRECT) }
|
||||
)
|
||||
}
|
||||
if (!viewModel.noSecondSlot) {
|
||||
MethodRadioRow(
|
||||
label = stringResource(CoreR.string.install_inactive_slot),
|
||||
selected = uiState.method == InstallViewModel.Method.INACTIVE_SLOT,
|
||||
onClick = { viewModel.selectMethod(InstallViewModel.Method.INACTIVE_SLOT) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NotesCard(notes: String) {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = notes,
|
||||
style = MiuixTheme.textStyles.body2,
|
||||
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CheckboxRow(label: String, checked: Boolean, onCheckedChange: (Boolean) -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = { onCheckedChange(it) }
|
||||
)
|
||||
Text(
|
||||
text = label,
|
||||
style = MiuixTheme.textStyles.body1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MethodRadioRow(label: String, selected: Boolean, onClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Checkbox(
|
||||
checked = selected,
|
||||
onCheckedChange = { onClick() }
|
||||
)
|
||||
Text(
|
||||
text = label,
|
||||
style = MiuixTheme.textStyles.body1,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
package com.topjohnwu.magisk.ui.install
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Spanned
|
||||
import android.text.SpannedString
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
|
||||
@@ -20,12 +13,15 @@ import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.base.ContentResultCallback
|
||||
import com.topjohnwu.magisk.core.ktx.toast
|
||||
import com.topjohnwu.magisk.core.repository.NetworkService
|
||||
import com.topjohnwu.magisk.databinding.set
|
||||
import com.topjohnwu.magisk.dialog.SecondSlotWarningDialog
|
||||
import com.topjohnwu.magisk.events.GetContentEvent
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -36,36 +32,24 @@ import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() {
|
||||
|
||||
enum class Method { NONE, PATCH, DIRECT, INACTIVE_SLOT }
|
||||
|
||||
data class UiState(
|
||||
val step: Int = 0,
|
||||
val method: Method = Method.NONE,
|
||||
val notes: String = "",
|
||||
val patchUri: Uri? = null,
|
||||
)
|
||||
|
||||
val isRooted get() = Info.isRooted
|
||||
val skipOptions = Info.isEmulator || (Info.isSAR && !Info.isFDE && Info.ramdisk)
|
||||
val noSecondSlot = !isRooted || !Info.isAB || Info.isEmulator
|
||||
|
||||
@get:Bindable
|
||||
var step = if (skipOptions) 1 else 0
|
||||
set(value) = set(value, field, { field = it }, BR.step)
|
||||
|
||||
private var methodId = -1
|
||||
|
||||
@get:Bindable
|
||||
var method
|
||||
get() = methodId
|
||||
set(value) = set(value, methodId, { methodId = it }, BR.method) {
|
||||
when (it) {
|
||||
R.id.method_patch -> {
|
||||
GetContentEvent("*/*", UriCallback()).publish()
|
||||
}
|
||||
R.id.method_inactive_slot -> {
|
||||
SecondSlotWarningDialog().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
private val _uiState = MutableStateFlow(UiState(step = if (skipOptions) 1 else 0))
|
||||
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
|
||||
|
||||
val data: LiveData<Uri?> get() = uri
|
||||
|
||||
@get:Bindable
|
||||
var notes: Spanned = SpannedString("")
|
||||
set(value) = set(value, field, { field = it }, BR.notes)
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
@@ -81,44 +65,53 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
||||
}
|
||||
val spanned = markwon.toMarkdown(noteText)
|
||||
withContext(Dispatchers.Main) {
|
||||
notes = spanned
|
||||
_uiState.update { it.copy(notes = spanned.toString()) }
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
uri.observeForever { newUri ->
|
||||
_uiState.update { it.copy(patchUri = newUri) }
|
||||
}
|
||||
}
|
||||
|
||||
fun nextStep() {
|
||||
_uiState.update { it.copy(step = 1) }
|
||||
}
|
||||
|
||||
fun selectMethod(method: Method) {
|
||||
_uiState.update { it.copy(method = method) }
|
||||
when (method) {
|
||||
Method.PATCH -> {
|
||||
GetContentEvent("*/*", UriCallback()).publish()
|
||||
}
|
||||
Method.INACTIVE_SLOT -> {
|
||||
SecondSlotWarningDialog().show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun install() {
|
||||
when (method) {
|
||||
R.id.method_patch -> FlashFragment.patch(data.value!!).navigate(true)
|
||||
R.id.method_direct -> FlashFragment.flash(false).navigate(true)
|
||||
R.id.method_inactive_slot -> FlashFragment.flash(true).navigate(true)
|
||||
else -> error("Unknown value")
|
||||
when (_uiState.value.method) {
|
||||
Method.PATCH -> FlashFragment.patch(data.value!!).navigate(true)
|
||||
Method.DIRECT -> FlashFragment.flash(false).navigate(true)
|
||||
Method.INACTIVE_SLOT -> FlashFragment.flash(true).navigate(true)
|
||||
else -> error("Unknown method")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveState(state: Bundle) {
|
||||
state.putParcelable(
|
||||
INSTALL_STATE_KEY, InstallState(
|
||||
methodId,
|
||||
step,
|
||||
Config.keepVerity,
|
||||
Config.keepEnc,
|
||||
Config.recovery
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRestoreState(state: Bundle) {
|
||||
state.getParcelable<InstallState>(INSTALL_STATE_KEY)?.let {
|
||||
methodId = it.method
|
||||
step = it.step
|
||||
Config.keepVerity = it.keepVerity
|
||||
Config.keepEnc = it.keepEnc
|
||||
Config.recovery = it.recovery
|
||||
val canInstall: Boolean
|
||||
get() {
|
||||
val state = _uiState.value
|
||||
return when (state.method) {
|
||||
Method.PATCH -> state.patchUri != null
|
||||
Method.DIRECT, Method.INACTIVE_SLOT -> true
|
||||
Method.NONE -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class UriCallback : ContentResultCallback {
|
||||
@@ -131,17 +124,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class InstallState(
|
||||
val method: Int,
|
||||
val step: Int,
|
||||
val keepVerity: Boolean,
|
||||
val keepEnc: Boolean,
|
||||
val recovery: Boolean,
|
||||
) : Parcelable
|
||||
|
||||
companion object {
|
||||
private const val INSTALL_STATE_KEY = "install_state"
|
||||
private val uri = MutableLiveData<Uri?>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,245 +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>
|
||||
|
||||
<import type="com.topjohnwu.magisk.core.Info" />
|
||||
|
||||
<import type="com.topjohnwu.magisk.core.Config" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.topjohnwu.magisk.ui.install.InstallViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
android:paddingBottom="@dimen/l2"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
tools:paddingTop="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l_50">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.skipOptions}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 0}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_options_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.setStep(1)}"
|
||||
android:text="@string/install_next" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
tools:layout_gravity="center">
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.isSAR}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepVerity}"
|
||||
android:text="@string/keep_dm_verity"
|
||||
tools:checked="true" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
goneUnless="@{Info.isFDE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepEnc}"
|
||||
android:text="@string/keep_force_encryption"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.ramdisk}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.recovery}"
|
||||
android:text="@string/recovery_mode"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 1}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_method_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 1}"
|
||||
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.install()}"
|
||||
android:text="@string/install_start"
|
||||
app:icon="@drawable/ic_forth_md2"
|
||||
app:iconGravity="textEnd" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
gone="@{viewModel.step != 1}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:checkedButton="@={viewModel.method}"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_patch"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/select_patch_file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_direct"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{!viewModel.rooted}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/direct_install" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_inactive_slot"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{viewModel.noSecondSlot}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_inactive_slot" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.notes.length == 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/release_notes"
|
||||
markdownText="@{viewModel.notes}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15dp"
|
||||
android:breakStrategy="simple"
|
||||
android:hyphenationFrequency="none"
|
||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:maxLines="5"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</layout>
|
||||
@@ -59,8 +59,7 @@
|
||||
<fragment
|
||||
android:id="@+id/installFragment"
|
||||
android:name="com.topjohnwu.magisk.ui.install.InstallFragment"
|
||||
android:label="InstallFragment"
|
||||
tools:layout="@layout/fragment_install_md2" />
|
||||
android:label="InstallFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/logFragment"
|
||||
|
||||
Reference in New Issue
Block a user