1
mirror of https://github.com/topjohnwu/Magisk synced 2025-11-09 00:32:30 +01:00

Compare commits

..

39 Commits
v25.0 ... v25.1

Author SHA1 Message Date
topjohnwu
bb64ba0ef6 Release Magisk v25.1 2022-06-19 02:54:22 -07:00
topjohnwu
d89a568897 Update v25.1 docs 2022-06-19 02:35:05 -07:00
topjohnwu
9fd1f41e8b Always relaunch process after package migration 2022-06-19 02:09:14 -07:00
孟武.尼德霍格.龍
c1ab348673 Improve Traditional Chinese strings
Co-authored-by: John Wu <topjohnwu@gmail.com>
2022-06-19 01:50:43 -07:00
canyie
00247c7901 Fix meizu non-SAR 2SI compatibility again
Meizu devices using 2SI won't switch root to /system and still on rootfs, and /init is the 1st stage's, which cannot handle the 2nd stage. So we have to manually execute /system/bin/init for the 2nd stage.
2022-06-19 01:22:18 -07:00
topjohnwu
3c75f474c6 Embed version info in prop format 2022-06-19 00:43:38 -07:00
topjohnwu
db1f5b0397 Reduce files relying on flags.h 2022-06-19 00:43:38 -07:00
fadlyas07
db277c3e55 app: l10n: Update Indonesian translations
* Added new strings based on the recent source.
* Fixed some words based on Indonesian National Dictionary (KBBI).

Link: https://kbbi.kemdikbud.go.id
Signed-off-by: fadlyas07 <mhmmdfdlyas@gmail.com>
2022-06-18 10:43:25 -07:00
vvb2060
b9c93c66f6 Force app version not lower than daemon 2022-06-17 11:53:16 -07:00
vvb2060
a250e2b56c Set version comment in apk 2022-06-17 11:53:16 -07:00
残页
cd96454886 Fix finding recovery image on direct install
Fix #5972, fix #5673
2022-06-17 02:53:18 -07:00
topjohnwu
741b679306 Cleanup libbase 2022-06-17 02:36:04 -07:00
topjohnwu
90013e486d Use AtomicBoolean 2022-06-17 02:03:09 -07:00
LoveSy
4e2ecdb920 Fix env overflow
Fix #5989
2022-06-17 02:02:44 -07:00
topjohnwu
6e5df1f06b Abort when unsupported dtb is detected 2022-06-16 01:47:23 -07:00
topjohnwu
9469e79e3c Proper namespacing
The IDE will get confused when #include is in a namespace
2022-06-15 02:38:56 -07:00
topjohnwu
db78c20161 Add dtb test command 2022-06-15 02:26:50 -07:00
topjohnwu
1699da1754 Update help message and make behavior consistent 2022-06-14 21:19:17 -07:00
canyie
754e690274 Fix config backup for legacy SAR 2022-06-14 02:57:47 -07:00
topjohnwu
6f74ed6ceb Cleanup manager.sh 2022-06-13 01:21:24 -07:00
canyie
71205bc530 Anchor Snackbar above reboot FAB in FlashFragment 2022-06-12 11:00:36 -07:00
Chris Renshaw
10e236abdf scripts: fix remaining instances of && ||
Looks like I may have missed this in ce84f1762c originally
2022-06-12 11:00:09 -07:00
残页
2248af00f3 Fix #5673
util_functions.sh overrides `get_flags` function (defined in manager.sh), which sets `RECOVERYMODE` and causes `check_boot_ramdisk` not overriding the incorrect value.
2022-06-12 00:32:34 -07:00
topjohnwu
7e61716277 Update Kotlin to 1.7.0 2022-06-11 03:41:02 -07:00
topjohnwu
50edb8d072 Better network detection and invalidation 2022-06-10 04:25:34 -07:00
topjohnwu
515f81944c Move coroutine job into its own class 2022-06-10 04:12:31 -07:00
topjohnwu
46d4708386 Decouple state from BaseViewModel 2022-06-10 02:13:25 -07:00
topjohnwu
aabc36f86b Maintain separate flash screen state 2022-06-10 00:33:53 -07:00
nikk gitanes
e0d5d90267 fix restart button focus on flash result 2022-06-10 00:33:53 -07:00
topjohnwu
482a5b991b Don't always refresh on network state change 2022-06-09 23:28:46 -07:00
CDzungx
20124fe410 Update vi translation 2022-06-09 21:03:26 -07:00
Softastur
f8dcec116a Fix Asturian translation 2022-06-09 21:03:09 -07:00
Ilya Kushnir
343a339aae Update RU strings (fix) 2022-06-09 21:02:45 -07:00
vvb2060
42606efe56 Always remove task 2022-06-09 21:02:31 -07:00
vvb2060
cae58c8790 Update hijack bins 2022-06-08 23:30:22 -07:00
topjohnwu
3a39dd4049 Update ramdisk restore implementation 2022-06-08 23:23:39 -07:00
canyie
89ff3c6572 Don't backup ramdisk created by Magisk
Fix topjohnwu#5938, fix topjohnwu#5944
2022-06-08 04:53:43 -07:00
topjohnwu
7bf9c74216 Don't skip backup even if original does not exist
Close #5945, fix #5944
2022-06-08 03:58:25 -07:00
topjohnwu
e2f3753551 Release new canary build 2022-06-07 03:36:21 -07:00
65 changed files with 726 additions and 566 deletions

View File

@@ -19,7 +19,7 @@ Some highlight features:
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads. [Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
[![](https://img.shields.io/badge/Magisk-v24.3-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v24.3) [![](https://img.shields.io/badge/Magisk-v24.3-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v24.3)
[![](https://img.shields.io/badge/Magisk%20Beta-v24.3-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v24.3) [![](https://img.shields.io/badge/Magisk%20Beta-v25.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v25.0)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-release.apk) [![](https://img.shields.io/badge/Magisk-Canary-red)](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-release.apk)
[![](https://img.shields.io/badge/Magisk-Debug-red)](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk) [![](https://img.shields.io/badge/Magisk-Debug-red)](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)

View File

@@ -97,7 +97,7 @@ dependencies {
implementation("com.squareup.moshi:moshi:${vMoshi}") implementation("com.squareup.moshi:moshi:${vMoshi}")
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}") kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
val vRoom = "2.4.2" val vRoom = "2.5.0-alpha02"
implementation("androidx.room:room-runtime:${vRoom}") implementation("androidx.room:room-runtime:${vRoom}")
implementation("androidx.room:room-ktx:${vRoom}") implementation("androidx.room:room-ktx:${vRoom}")
kapt("androidx.room:room-compiler:${vRoom}") kapt("androidx.room:room-compiler:${vRoom}")
@@ -109,12 +109,12 @@ dependencies {
implementation("androidx.biometric:biometric:1.1.0") implementation("androidx.biometric:biometric:1.1.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.appcompat:appcompat:1.4.1") implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.preference:preference:1.2.0") implementation("androidx.preference:preference:1.2.0")
implementation("androidx.recyclerview:recyclerview:1.2.1") implementation("androidx.recyclerview:recyclerview:1.2.1")
implementation("androidx.fragment:fragment-ktx:1.4.1") implementation("androidx.fragment:fragment-ktx:1.4.1")
implementation("androidx.transition:transition:1.4.1") implementation("androidx.transition:transition:1.4.1")
implementation("androidx.core:core-ktx:1.7.0") implementation("androidx.core:core-ktx:1.8.0")
implementation("androidx.core:core-splashscreen:1.0.0-rc01") implementation("androidx.core:core-splashscreen:1.0.0-rc01")
implementation("com.google.android.material:material:1.6.0") implementation("com.google.android.material:material:1.6.1")
} }

View File

@@ -27,6 +27,7 @@
android:name=".ui.surequest.SuRequestActivity" android:name=".ui.surequest.SuRequestActivity"
android:directBootAware="true" android:directBootAware="true"
android:exported="false" android:exported="false"
android:taskAffinity=""
tools:ignore="AppLinkUrlError"> tools:ignore="AppLinkUrlError">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />

View File

@@ -0,0 +1,22 @@
package com.topjohnwu.magisk.arch
import androidx.annotation.MainThread
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
abstract class AsyncLoadViewModel : BaseViewModel() {
private var loadingJob: Job? = null
@MainThread
fun startLoading() {
if (loadingJob?.isActive == true) {
// Prevent multiple jobs from running at the same time
return
}
loadingJob = viewModelScope.launch { doLoadWork() }
}
protected abstract suspend fun doLoadWork()
}

View File

@@ -76,7 +76,10 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.requestRefresh() viewModel.let {
if (it is AsyncLoadViewModel)
it.startLoading()
}
} }
protected open fun onPreBind(binding: Binding) { protected open fun onPreBind(binding: Binding) {

View File

@@ -4,61 +4,29 @@ import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.databinding.Bindable
import androidx.databinding.PropertyChangeRegistry import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableHost import com.topjohnwu.magisk.databinding.ObservableHost
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.events.BackPressEvent import com.topjohnwu.magisk.events.BackPressEvent
import com.topjohnwu.magisk.events.NavigationEvent import com.topjohnwu.magisk.events.NavigationEvent
import com.topjohnwu.magisk.events.PermissionEvent import com.topjohnwu.magisk.events.PermissionEvent
import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.events.SnackbarEvent
import kotlinx.coroutines.Job
abstract class BaseViewModel( abstract class BaseViewModel : ViewModel(), ObservableHost {
initialState: State = State.LOADING
) : ViewModel(), ObservableHost {
override var callbacks: PropertyChangeRegistry? = null override var callbacks: PropertyChangeRegistry? = null
enum class State {
LOADED, LOADING, LOADING_FAILED
}
@get:Bindable
val loading get() = state == State.LOADING
@get:Bindable
val loaded get() = state == State.LOADED
@get:Bindable
val loadFailed get() = state == State.LOADING_FAILED
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
var state= initialState
set(value) = set(value, field, { field = it }, BR.loading, BR.loaded, BR.loadFailed)
private val _viewEvents = MutableLiveData<ViewEvent>() private val _viewEvents = MutableLiveData<ViewEvent>()
private var runningJob: Job? = null val viewEvents: LiveData<ViewEvent> get() = _viewEvents
open fun onSaveState(state: Bundle) {} open fun onSaveState(state: Bundle) {}
open fun onRestoreState(state: Bundle) {} open fun onRestoreState(state: Bundle) {}
open fun onNetworkChanged(network: Boolean) {}
/** This should probably never be called manually, it's called manually via delegate. */
@Synchronized
fun requestRefresh() {
if (runningJob?.isActive == true) {
return
}
runningJob = refresh()
}
protected open fun refresh(): Job? = null
fun withPermission(permission: String, callback: (Boolean) -> Unit) { fun withPermission(permission: String, callback: (Boolean) -> Unit) {
PermissionEvent(permission, callback).publish() PermissionEvent(permission, callback).publish()

View File

@@ -89,7 +89,10 @@ abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModel
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.requestRefresh() viewModel.let {
if (it is AsyncLoadViewModel)
it.startLoading()
}
} }
override fun onEventDispatched(event: ViewEvent) = when (event) { override fun onEventDispatched(event: ViewEvent) = when (event) {

View File

@@ -17,12 +17,8 @@ interface ViewModelHolder : LifecycleOwner, ViewModelStoreOwner {
val viewModel: BaseViewModel val viewModel: BaseViewModel
fun startObserveLiveData() { fun startObserveLiveData() {
viewModel.viewEvents.observe(this) { viewModel.viewEvents.observe(this, this::onEventDispatched)
onEventDispatched(it) Info.isConnected.observe(this, viewModel::onNetworkChanged)
}
Info.isConnected.observe(this) {
viewModel.requestRefresh()
}
} }
/** /**

View File

@@ -10,7 +10,6 @@ import com.topjohnwu.magisk.core.repository.NetworkService
import com.topjohnwu.magisk.core.utils.net.NetworkObserver import com.topjohnwu.magisk.core.utils.net.NetworkObserver
import com.topjohnwu.magisk.ktx.getProperty import com.topjohnwu.magisk.ktx.getProperty
import com.topjohnwu.superuser.ShellUtils.fastCmd import com.topjohnwu.superuser.ShellUtils.fastCmd
import com.topjohnwu.superuser.internal.UiThreadHandler
val isRunningAsStub get() = Info.stub != null val isRunningAsStub get() = Info.stub != null
@@ -47,7 +46,8 @@ object Info {
val isConnected: LiveData<Boolean> by lazy { val isConnected: LiveData<Boolean> by lazy {
MutableLiveData(false).also { field -> MutableLiveData(false).also { field ->
NetworkObserver.observe(AppContext) { NetworkObserver.observe(AppContext) {
UiThreadHandler.run { field.value = it } remote = EMPTY_REMOTE
field.postValue(it)
} }
} }
} }

View File

@@ -46,7 +46,10 @@ class NetworkService(
private inline fun <T> safe(factory: () -> T): T? { private inline fun <T> safe(factory: () -> T): T? {
return try { return try {
factory() if (Info.isConnected.value == true)
factory()
else
null
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
null null

View File

@@ -37,6 +37,7 @@ import java.io.*
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipFile import java.util.zip.ZipFile
@@ -57,7 +58,7 @@ abstract class MagiskInstallImpl protected constructor(
private val localFS get() = FileSystemManager.getLocal() private val localFS get() = FileSystemManager.getLocal()
private fun findImage(): Boolean { private fun findImage(): Boolean {
val bootPath = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh() val bootPath = "RECOVERYMODE=${Config.recovery} find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
if (bootPath.isEmpty()) { if (bootPath.isEmpty()) {
console.add("! Unable to detect target image") console.add("! Unable to detect target image")
return false return false
@@ -420,20 +421,15 @@ abstract class MagiskInstallImpl protected constructor(
protected abstract suspend fun operations(): Boolean protected abstract suspend fun operations(): Boolean
open suspend fun exec(): Boolean { open suspend fun exec(): Boolean {
synchronized(Companion) { if (haveActiveSession.getAndSet(true))
if (haveActiveSession) return false
return false
haveActiveSession = true
}
val result = withContext(Dispatchers.IO) { operations() } val result = withContext(Dispatchers.IO) { operations() }
synchronized(Companion) { haveActiveSession.set(false)
haveActiveSession = false
}
return result return result
} }
companion object { companion object {
private var haveActiveSession = false private var haveActiveSession = AtomicBoolean(false)
} }
} }

View File

@@ -9,6 +9,7 @@ import androidx.databinding.ViewDataBinding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.arch.NavigationActivity import com.topjohnwu.magisk.arch.NavigationActivity
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
@@ -98,10 +99,10 @@ abstract class SplashActivity<Binding : ViewDataBinding> : NavigationActivity<Bi
Config.load(prevPkg) Config.load(prevPkg)
handleRepackage(prevPkg) handleRepackage(prevPkg)
if (prevPkg != null && !isRunningAsStub) { if (prevPkg != null) {
runOnUiThread { runOnUiThread {
// Might have new configuration loaded, relaunch the activity // Relaunch the process after package migration
relaunch() StubApk.restartProcess(this)
} }
return return
} }

View File

@@ -2,23 +2,22 @@ package com.topjohnwu.magisk.ui.deny
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import androidx.lifecycle.viewModelScope import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.arch.BaseViewModel import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.databinding.bindExtra import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.filterableListOf import com.topjohnwu.magisk.databinding.filterableListOf
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.ktx.concurrentMap import com.topjohnwu.magisk.ktx.concurrentMap
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.toCollection import kotlinx.coroutines.flow.toCollection
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class DenyListViewModel : BaseViewModel() { class DenyListViewModel : AsyncLoadViewModel() {
var isShowSystem = false var isShowSystem = false
set(value) { set(value) {
@@ -43,13 +42,13 @@ class DenyListViewModel : BaseViewModel() {
it.put(BR.viewModel, this) it.put(BR.viewModel, this)
} }
@get:Bindable
var loading = true
private set(value) = set(value, field, { field = it }, BR.loading)
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
override fun refresh() = viewModelScope.launch { override suspend fun doLoadWork() {
if (!Utils.showSuperUser()) { loading = true
state = State.LOADING_FAILED
return@launch
}
state = State.LOADING
val (apps, diff) = withContext(Dispatchers.Default) { val (apps, diff) = withContext(Dispatchers.Default) {
val pm = AppContext.packageManager val pm = AppContext.packageManager
val denyList = Shell.cmd("magisk --denylist ls").exec().out val denyList = Shell.cmd("magisk --denylist ls").exec().out
@@ -84,6 +83,6 @@ class DenyListViewModel : BaseViewModel() {
(it.isChecked || (filterSystem() && filterOS())) && filterQuery() (it.isChecked || (filterSystem() && filterOS())) && filterQuery()
} }
state = State.LOADED loading = false
} }
} }

View File

@@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import androidx.core.view.isVisible
import androidx.navigation.NavDeepLinkBuilder import androidx.navigation.NavDeepLinkBuilder
import com.topjohnwu.magisk.MainDirections import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
@@ -21,6 +22,8 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
override val layoutRes = R.layout.fragment_flash_md2 override val layoutRes = R.layout.fragment_flash_md2
override val viewModel by viewModel<FlashViewModel>() override val viewModel by viewModel<FlashViewModel>()
override val snackbarView: View get() = binding.snackbarContainer override val snackbarView: View get() = binding.snackbarContainer
override val snackbarAnchorView: View?
get() = if (binding.restartBtn.isShown) binding.restartBtn else super.snackbarAnchorView
private var defaultOrientation = -1 private var defaultOrientation = -1
@@ -34,8 +37,20 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
setHasOptionsMenu(true) setHasOptionsMenu(true)
activity?.setTitle(R.string.flash_screen_title) activity?.setTitle(R.string.flash_screen_title)
viewModel.subtitle.observe(this) { viewModel.state.observe(this) {
activity?.supportActionBar?.setSubtitle(it) activity?.supportActionBar?.setSubtitle(
when (it) {
FlashViewModel.State.FLASHING -> R.string.flashing
FlashViewModel.State.SUCCESS -> R.string.done
FlashViewModel.State.FAILED -> R.string.failure
}
)
if (it == FlashViewModel.State.SUCCESS && viewModel.showReboot) {
binding.restartBtn.apply {
if (!this.isVisible) this.show()
if (!this.isFocused) this.requestFocus()
}
}
} }
} }
@@ -66,7 +81,7 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
} }
override fun onKeyEvent(event: KeyEvent): Boolean { override fun onKeyEvent(event: KeyEvent): Boolean {
return when(event.keyCode) { return when (event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_UP,
KeyEvent.KEYCODE_VOLUME_DOWN -> true KeyEvent.KEYCODE_VOLUME_DOWN -> true
else -> false else -> false
@@ -74,7 +89,8 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>() {
} }
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
if (viewModel.loading) return true if (viewModel.flashing.value == true)
return true
return super.onBackPressed() return super.onBackPressed()
} }

View File

@@ -4,6 +4,7 @@ import android.view.MenuItem
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
@@ -27,13 +28,18 @@ import kotlinx.coroutines.launch
class FlashViewModel : BaseViewModel() { class FlashViewModel : BaseViewModel() {
enum class State {
FLASHING, SUCCESS, FAILED
}
private val _state = MutableLiveData(State.FLASHING)
val state: LiveData<State> get() = _state
val flashing = Transformations.map(state) { it == State.FLASHING }
@get:Bindable @get:Bindable
var showReboot = Info.isRooted var showReboot = Info.isRooted
set(value) = set(value, field, { field = it }, BR.showReboot) set(value) = set(value, field, { field = it }, BR.showReboot)
private val _subtitle = MutableLiveData(R.string.flashing)
val subtitle get() = _subtitle as LiveData<Int>
val items = diffListOf<ConsoleItem>() val items = diffListOf<ConsoleItem>()
lateinit var args: FlashFragmentArgs lateinit var args: FlashFragmentArgs
@@ -83,11 +89,7 @@ class FlashViewModel : BaseViewModel() {
} }
private fun onResult(success: Boolean) { private fun onResult(success: Boolean) {
state = if (success) State.LOADED else State.LOADING_FAILED _state.value = if (success) State.SUCCESS else State.FAILED
when {
success -> _subtitle.postValue(R.string.done)
else -> _subtitle.postValue(R.string.failure)
}
} }
fun onMenuItemClicked(item: MenuItem): Boolean { fun onMenuItemClicked(item: MenuItem): Boolean {
@@ -100,7 +102,8 @@ class FlashViewModel : BaseViewModel() {
private fun savePressed() = withExternalRW { private fun savePressed() = withExternalRW {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val name = "magisk_install_log_%s.log".format( val name = "magisk_install_log_%s.log".format(
System.currentTimeMillis().toTime(timeFormatStandard)) System.currentTimeMillis().toTime(timeFormatStandard)
)
val file = MediaStoreUtils.getFile(name, true) val file = MediaStoreUtils.getFile(name, true)
file.uri.outputStream().bufferedWriter().use { writer -> file.uri.outputStream().bufferedWriter().use { writer ->
synchronized(logItems) { synchronized(logItems) {

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.home
import android.content.Context import android.content.Context
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
@@ -23,16 +22,15 @@ import com.topjohnwu.magisk.ktx.await
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.asText import com.topjohnwu.magisk.utils.asText
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
enum class MagiskState {
NOT_INSTALLED, UP_TO_DATE, OBSOLETE, LOADING
}
class HomeViewModel( class HomeViewModel(
private val svc: NetworkService private val svc: NetworkService
) : BaseViewModel() { ) : AsyncLoadViewModel() {
enum class State {
LOADING, INVALID, OUTDATED, UP_TO_DATE
}
val magiskTitleBarrierIds = val magiskTitleBarrierIds =
intArrayOf(R.id.home_magisk_icon, R.id.home_magisk_title, R.id.home_magisk_button) intArrayOf(R.id.home_magisk_icon, R.id.home_magisk_title, R.id.home_magisk_button)
@@ -43,16 +41,16 @@ class HomeViewModel(
var isNoticeVisible = Config.safetyNotice var isNoticeVisible = Config.safetyNotice
set(value) = set(value, field, { field = it }, BR.noticeVisible) set(value) = set(value, field, { field = it }, BR.noticeVisible)
val stateMagisk val magiskState
get() = when { get() = when {
!Info.env.isActive -> MagiskState.NOT_INSTALLED !Info.env.isActive -> State.INVALID
Info.env.versionCode < BuildConfig.VERSION_CODE -> MagiskState.OBSOLETE Info.env.versionCode < BuildConfig.VERSION_CODE -> State.OUTDATED
else -> MagiskState.UP_TO_DATE else -> State.UP_TO_DATE
} }
@get:Bindable @get:Bindable
var stateManager = MagiskState.LOADING var appState = State.LOADING
set(value) = set(value, field, { field = it }, BR.stateManager) set(value) = set(value, field, { field = it }, BR.appState)
val magiskInstalledVersion val magiskInstalledVersion
get() = Info.env.run { get() = Info.env.run {
@@ -83,14 +81,12 @@ class HomeViewModel(
private var checkedEnv = false private var checkedEnv = false
} }
override fun refresh() = viewModelScope.launch { override suspend fun doLoadWork() {
state = State.LOADING appState = State.LOADING
Info.getRemote(svc)?.apply { Info.getRemote(svc)?.apply {
state = State.LOADED appState = when {
BuildConfig.VERSION_CODE < magisk.versionCode -> State.OUTDATED
stateManager = when { else -> State.UP_TO_DATE
BuildConfig.VERSION_CODE < magisk.versionCode -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE
} }
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
@@ -98,19 +94,12 @@ class HomeViewModel(
("${magisk.version} (${magisk.versionCode}) (${stub.versionCode})" + ("${magisk.version} (${magisk.versionCode}) (${stub.versionCode})" +
if (isDebug) " (D)" else "").asText() if (isDebug) " (D)" else "").asText()
} ?: run { } ?: run {
state = State.LOADING_FAILED
managerRemoteVersion = R.string.not_available.asText() managerRemoteVersion = R.string.not_available.asText()
} }
ensureEnv() ensureEnv()
} }
val showTest = false override fun onNetworkChanged(network: Boolean) = startLoading()
fun onTestPressed() = object : ViewEvent(), ActivityExecutor {
override fun invoke(activity: UIActivity<*>) {
/* Entry point to trigger test events within the app */
}
}.publish()
fun onProgressUpdate(progress: Float, subject: Subject) { fun onProgressUpdate(progress: Float, subject: Subject) {
if (subject is App) if (subject is App)
@@ -123,14 +112,14 @@ class HomeViewModel(
fun onDeletePressed() = UninstallDialog().publish() fun onDeletePressed() = UninstallDialog().publish()
fun onManagerPressed() = when (state) { fun onManagerPressed() = when (magiskState) {
State.LOADED -> withExternalRW { State.LOADING -> SnackbarEvent(R.string.loading).publish()
State.INVALID -> SnackbarEvent(R.string.no_connection).publish()
else -> withExternalRW {
withInstallPermission { withInstallPermission {
ManagerInstallDialog().publish() ManagerInstallDialog().publish()
} }
} }
State.LOADING -> SnackbarEvent(R.string.loading).publish()
else -> SnackbarEvent(R.string.no_connection).publish()
} }
fun onMagiskPressed() = withExternalRW { fun onMagiskPressed() = withExternalRW {
@@ -143,7 +132,7 @@ class HomeViewModel(
} }
private suspend fun ensureEnv() { private suspend fun ensureEnv() {
if (MagiskState.NOT_INSTALLED == stateMagisk || checkedEnv) return if (magiskState == State.INVALID || checkedEnv) return
val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}" val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}"
if (!Shell.cmd(cmd).await().isSuccess) { if (!Shell.cmd(cmd).await().isSuccess) {
EnvFixDialog(this).publish() EnvFixDialog(this).publish()
@@ -151,4 +140,10 @@ class HomeViewModel(
checkedEnv = true checkedEnv = true
} }
val showTest = false
fun onTestPressed() = object : ViewEvent(), ActivityExecutor {
override fun invoke(activity: UIActivity<*>) {
/* Entry point to trigger test events within the app */
}
}.publish()
} }

View File

@@ -95,7 +95,6 @@ class InstallViewModel(
R.id.method_inactive_slot -> FlashFragment.flash(true).navigate(true) R.id.method_inactive_slot -> FlashFragment.flash(true).navigate(true)
else -> error("Unknown value") else -> error("Unknown value")
} }
state = State.LOADING
} }
override fun onSaveState(state: Bundle) { override fun onSaveState(state: Bundle) {

View File

@@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.repository.LogRepository import com.topjohnwu.magisk.core.repository.LogRepository
import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils
@@ -24,7 +24,7 @@ import java.io.FileInputStream
class LogViewModel( class LogViewModel(
private val repo: LogRepository private val repo: LogRepository
) : BaseViewModel() { ) : AsyncLoadViewModel() {
// --- empty view // --- empty view
@@ -43,7 +43,7 @@ class LogViewModel(
var consoleText = " " var consoleText = " "
set(value) = set(value, field, { field = it }, BR.consoleText) set(value) = set(value, field, { field = it }, BR.consoleText)
override fun refresh() = viewModelScope.launch { override suspend fun doLoadWork() {
consoleText = repo.fetchMagiskLogs() consoleText = repo.fetchMagiskLogs()
val (suLogs, diff) = withContext(Dispatchers.Default) { val (suLogs, diff) = withContext(Dispatchers.Default) {
val suLogs = repo.fetchSuLogs().map { LogRvItem(it) } val suLogs = repo.fetchSuLogs().map { LogRvItem(it) }
@@ -89,12 +89,12 @@ class LogViewModel(
fun clearMagiskLog() = repo.clearMagiskLogs { fun clearMagiskLog() = repo.clearMagiskLogs {
SnackbarEvent(R.string.logs_cleared).publish() SnackbarEvent(R.string.logs_cleared).publish()
requestRefresh() startLoading()
} }
fun clearLog() = viewModelScope.launch { fun clearLog() = viewModelScope.launch {
repo.clearLogs() repo.clearLogs()
SnackbarEvent(R.string.logs_cleared).publish() SnackbarEvent(R.string.logs_cleared).publish()
requestRefresh() startLoading()
} }
} }

View File

@@ -1,29 +1,24 @@
package com.topjohnwu.magisk.ui.module package com.topjohnwu.magisk.ui.module
import android.net.Uri import android.net.Uri
import androidx.databinding.Bindable
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.ContentResultCallback import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.core.model.module.LocalModule import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.databinding.MergeObservableList import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.diffListOf
import com.topjohnwu.magisk.events.GetContentEvent import com.topjohnwu.magisk.events.GetContentEvent
import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
class ModuleViewModel : BaseViewModel() { class ModuleViewModel : AsyncLoadViewModel() {
val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove) val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove)
@@ -36,6 +31,10 @@ class ModuleViewModel : BaseViewModel() {
val data get() = uri val data get() = uri
@get:Bindable
var loading = true
private set(value) = set(value, field, { field = it }, BR.loading)
init { init {
if (Info.env.isActive && LocalModule.loaded()) { if (Info.env.isActive && LocalModule.loaded()) {
items.insertItem(InstallModule) items.insertItem(InstallModule)
@@ -43,15 +42,15 @@ class ModuleViewModel : BaseViewModel() {
} }
} }
override fun refresh(): Job { override suspend fun doLoadWork() {
return viewModelScope.launch { loading = true
state = State.LOADING loadInstalled()
loadInstalled() loading = false
state = State.LOADED loadUpdateInfo()
loadUpdateInfo()
}
} }
override fun onNetworkChanged(network: Boolean) = startLoading()
private suspend fun loadInstalled() { private suspend fun loadInstalled() {
val installed = LocalModule.installed().map { LocalModuleRvItem(it) } val installed = LocalModule.installed().map { LocalModuleRvItem(it) }
val diff = withContext(Dispatchers.Default) { val diff = withContext(Dispatchers.Default) {

View File

@@ -4,20 +4,18 @@ import android.annotation.SuppressLint
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import android.os.Process import android.os.Process
import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList import androidx.databinding.ObservableArrayList
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao
import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.model.su.SuPolicy import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.core.utils.BiometricHelper import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.databinding.AnyDiffRvItem import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.databinding.MergeObservableList
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.diffListOf
import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.BiometricEvent import com.topjohnwu.magisk.events.dialog.BiometricEvent
import com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog import com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog
@@ -31,7 +29,7 @@ import kotlinx.coroutines.withContext
class SuperuserViewModel( class SuperuserViewModel(
private val db: PolicyDao private val db: PolicyDao
) : BaseViewModel() { ) : AsyncLoadViewModel() {
private val itemNoData = TextItem(R.string.superuser_policy_none) private val itemNoData = TextItem(R.string.superuser_policy_none)
@@ -45,15 +43,17 @@ class SuperuserViewModel(
it.put(BR.listener, this) it.put(BR.listener, this)
} }
// --- @get:Bindable
var loading = true
private set(value) = set(value, field, { field = it }, BR.loading)
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
override fun refresh() = viewModelScope.launch { override suspend fun doLoadWork() {
if (!Utils.showSuperUser()) { if (!Utils.showSuperUser()) {
state = State.LOADING_FAILED loading = false
return@launch return
} }
state = State.LOADING loading = true
val (policies, diff) = withContext(Dispatchers.IO) { val (policies, diff) = withContext(Dispatchers.IO) {
db.deleteOutdated() db.deleteOutdated()
db.delete(AppContext.applicationInfo.uid) db.delete(AppContext.applicationInfo.uid)
@@ -98,7 +98,7 @@ class SuperuserViewModel(
itemsHelpers.clear() itemsHelpers.clear()
else if (itemsHelpers.isEmpty()) else if (itemsHelpers.isEmpty())
itemsHelpers.add(itemNoData) itemsHelpers.add(itemNoData)
state = State.LOADED loading = false
} }
// --- // ---

View File

@@ -24,13 +24,9 @@ open class SuRequestActivity : UIActivity<ActivityRequestBinding>() {
override val layoutRes: Int = R.layout.activity_request override val layoutRes: Int = R.layout.activity_request
override val viewModel: SuRequestViewModel by viewModel() override val viewModel: SuRequestViewModel by viewModel()
override fun onBackPressed() {
viewModel.denyPressed()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE) supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
lockOrientation() requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.addFlags(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) window.addFlags(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -62,7 +58,11 @@ open class SuRequestActivity : UIActivity<ActivityRequestBinding>() {
return theme return theme
} }
private fun lockOrientation() { override fun onBackPressed() {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED viewModel.denyPressed()
}
override fun finish() {
super.finishAndRemoveTask()
} }
} }

View File

@@ -24,7 +24,7 @@
android:paddingTop="@dimen/internal_action_bar_size" android:paddingTop="@dimen/internal_action_bar_size"
app:fitsSystemWindowsInsets="top|bottom" app:fitsSystemWindowsInsets="top|bottom"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:invisibleUnless="@{viewModel.loaded}" app:invisible="@{viewModel.loading}"
app:items="@{viewModel.items}" app:items="@{viewModel.items}"
app:extraBindings="@{viewModel.extraBindings}" app:extraBindings="@{viewModel.extraBindings}"
tools:listitem="@layout/item_hide_md2" tools:listitem="@layout/item_hide_md2"
@@ -52,24 +52,6 @@
</LinearLayout> </LinearLayout>
<LinearLayout
goneUnless="@{viewModel.loadFailed}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
tools:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_available"
android:textAppearance="@style/AppearanceFoundation.Title"
android:textStyle="bold" />
</LinearLayout>
</FrameLayout> </FrameLayout>
</layout> </layout>

View File

@@ -24,13 +24,13 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/flash_content" android:id="@+id/flash_content"
app:items="@{viewModel.items}"
scrollToLast="@{true}" scrollToLast="@{true}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
app:fitsSystemWindowsInsets="start|end|bottom" app:fitsSystemWindowsInsets="start|end|bottom"
app:items="@{viewModel.items}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/item_console_md2" /> tools:listitem="@layout/item_console_md2" />
@@ -38,27 +38,31 @@
</HorizontalScrollView> </HorizontalScrollView>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
gone="@{!viewModel.loaded || !viewModel.showReboot}" android:id="@+id/restart_btn"
gone="@{viewModel.flashing || !viewModel.showReboot}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="@dimen/l1" android:layout_margin="@dimen/l1"
android:layout_marginBottom="@dimen/l1" android:layout_marginBottom="@dimen/l1"
android:clickable="@{!viewModel.flashing}"
android:enabled="@{!viewModel.flashing}"
android:focusable="true"
android:onClick="@{() -> viewModel.restartPressed()}" android:onClick="@{() -> viewModel.restartPressed()}"
android:text="@string/reboot" android:text="@string/reboot"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="?colorOnPrimary" android:textColor="?colorOnPrimary"
android:textStyle="bold" android:textStyle="bold"
app:layout_fitsSystemWindowsInsets="bottom"
app:backgroundTint="?colorPrimary" app:backgroundTint="?colorPrimary"
app:icon="@drawable/ic_restart" app:icon="@drawable/ic_restart"
app:iconTint="?colorOnPrimary" /> app:iconTint="?colorOnPrimary"
app:layout_fitsSystemWindowsInsets="bottom" />
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/snackbar_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/snackbar_container" app:fitsSystemWindowsInsets="top|bottom" />
app:fitsSystemWindowsInsets="top|bottom"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -7,8 +7,6 @@
<import type="com.topjohnwu.magisk.core.Info" /> <import type="com.topjohnwu.magisk.core.Info" />
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
<import type="com.topjohnwu.magisk.ui.home.DeveloperItem" /> <import type="com.topjohnwu.magisk.ui.home.DeveloperItem" />
<import type="com.topjohnwu.magisk.ui.home.IconLink" /> <import type="com.topjohnwu.magisk.ui.home.IconLink" />

View File

@@ -19,7 +19,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/superuser_list" android:id="@+id/superuser_list"
goneUnless="@{viewModel.loaded || !viewModel.items.empty}" gone="@{viewModel.loading}"
app:items="@{viewModel.items}" app:items="@{viewModel.items}"
app:extraBindings="@{viewModel.extraBindings}" app:extraBindings="@{viewModel.extraBindings}"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -33,7 +33,7 @@
tools:listitem="@layout/item_policy_md2" /> tools:listitem="@layout/item_policy_md2" />
<LinearLayout <LinearLayout
goneUnless="@{viewModel.loading &amp;&amp; viewModel.items.empty}" goneUnless="@{viewModel.loading}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@@ -54,24 +54,6 @@
</LinearLayout> </LinearLayout>
<LinearLayout
goneUnless="@{viewModel.loadFailed}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
tools:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_available"
android:textAppearance="@style/AppearanceFoundation.Title"
android:textStyle="bold" />
</LinearLayout>
</FrameLayout> </FrameLayout>
</layout> </layout>

View File

@@ -7,7 +7,7 @@
<import type="com.topjohnwu.magisk.core.Info" /> <import type="com.topjohnwu.magisk.core.Info" />
<import type="com.topjohnwu.magisk.ui.home.MagiskState" /> <import type="com.topjohnwu.magisk.ui.home.HomeViewModel.State" />
<variable <variable
name="viewModel" name="viewModel"
@@ -63,7 +63,7 @@
<Button <Button
style="@style/WidgetFoundation.Button" style="@style/WidgetFoundation.Button"
gone="@{viewModel.stateMagisk != MagiskState.OBSOLETE}" gone="@{viewModel.magiskState != State.OUTDATED}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onMagiskPressed()}" android:onClick="@{() -> viewModel.onMagiskPressed()}"
@@ -74,7 +74,7 @@
<Button <Button
style="@style/WidgetFoundation.Button.Text" style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.stateMagisk == MagiskState.OBSOLETE}" gone="@{viewModel.magiskState == State.OUTDATED}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"

View File

@@ -7,7 +7,7 @@
<import type="com.topjohnwu.magisk.core.Info" /> <import type="com.topjohnwu.magisk.core.Info" />
<import type="com.topjohnwu.magisk.ui.home.MagiskState" /> <import type="com.topjohnwu.magisk.ui.home.HomeViewModel.State" />
<variable <variable
name="viewModel" name="viewModel"
@@ -64,7 +64,7 @@
<Button <Button
style="@style/WidgetFoundation.Button" style="@style/WidgetFoundation.Button"
gone="@{viewModel.stateManager != MagiskState.OBSOLETE}" gone="@{viewModel.appState != State.OUTDATED}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onManagerPressed()}" android:onClick="@{() -> viewModel.onManagerPressed()}"
@@ -75,7 +75,7 @@
<Button <Button
style="@style/WidgetFoundation.Button.Text" style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.stateManager != MagiskState.UP_TO_DATE}" gone="@{viewModel.appState != State.UP_TO_DATE}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"

View File

@@ -10,7 +10,7 @@ env_check() {
for file in busybox magiskboot magiskinit util_functions.sh boot_patch.sh; do for file in busybox magiskboot magiskinit util_functions.sh boot_patch.sh; do
[ -f "$MAGISKBIN/$file" ] || return 1 [ -f "$MAGISKBIN/$file" ] || return 1
done done
if [ "$2" -ge 24302 ]; then if [ "$2" -ge 25000 ]; then
[ -f "$MAGISKBIN/magiskpolicy" ] || return 1 [ -f "$MAGISKBIN/magiskpolicy" ] || return 1
fi fi
grep -xqF "MAGISK_VER='$1'" "$MAGISKBIN/util_functions.sh" || return 1 grep -xqF "MAGISK_VER='$1'" "$MAGISKBIN/util_functions.sh" || return 1
@@ -99,7 +99,8 @@ post_ota() {
rm -f $1 rm -f $1
chmod 755 bootctl chmod 755 bootctl
./bootctl hal-info || return ./bootctl hal-info || return
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0 SLOT_NUM=0
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1
./bootctl set-active-boot-slot $SLOT_NUM ./bootctl set-active-boot-slot $SLOT_NUM
cat << EOF > post-fs-data.d/post_ota.sh cat << EOF > post-fs-data.d/post_ota.sh
/data/adb/bootctl mark-boot-successful /data/adb/bootctl mark-boot-successful
@@ -142,18 +143,16 @@ adb_pm_install() {
check_boot_ramdisk() { check_boot_ramdisk() {
# Create boolean ISAB # Create boolean ISAB
[ -z $SLOT ] && ISAB=false || ISAB=true ISAB=true
[ -z $SLOT ] && ISAB=false
# If we are running as recovery mode, then we do not have ramdisk
[ "$RECOVERYMODE" = "true" ] && return 1
# If we are A/B, then we must have ramdisk # If we are A/B, then we must have ramdisk
$ISAB && return 0 $ISAB && return 0
# If we are using legacy SAR, but not A/B, assume we do not have ramdisk # If we are using legacy SAR, but not A/B, assume we do not have ramdisk
if grep ' / ' /proc/mounts | grep -q '/dev/root'; then if grep ' / ' /proc/mounts | grep -q '/dev/root'; then
# Override recovery mode to true if not set # Override recovery mode to true
[ -z $RECOVERYMODE ] && RECOVERYMODE=true RECOVERYMODE=true
return 1 return 1
fi fi
@@ -173,7 +172,8 @@ check_encryption() {
CRYPTOTYPE="file" CRYPTOTYPE="file"
else else
# We are either FDE or metadata encryption (which is also FBE) # We are either FDE or metadata encryption (which is also FBE)
grep -q ' /metadata ' /proc/mounts && CRYPTOTYPE="file" || CRYPTOTYPE="block" CRYPTOTYPE="block"
grep -q ' /metadata ' /proc/mounts && CRYPTOTYPE="file"
fi fi
fi fi
fi fi
@@ -189,12 +189,14 @@ check_encryption() {
mount_partitions() { mount_partitions() {
[ "$(getprop ro.build.ab_update)" = "true" ] && SLOT=$(getprop ro.boot.slot_suffix) [ "$(getprop ro.build.ab_update)" = "true" ] && SLOT=$(getprop ro.boot.slot_suffix)
# Check whether non rootfs root dir exists # Check whether non rootfs root dir exists
grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true || SYSTEM_ROOT=false SYSTEM_ROOT=false
grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true
} }
get_flags() { get_flags() {
KEEPVERITY=$SYSTEM_ROOT KEEPVERITY=$SYSTEM_ROOT
[ "$(getprop ro.crypto.state)" = "encrypted" ] && ISENCRYPTED=true || ISENCRYPTED=false ISENCRYPTED=false
[ "$(getprop ro.crypto.state)" = "encrypted" ] && ISENCRYPTED=true
KEEPFORCEENCRYPT=$ISENCRYPTED KEEPFORCEENCRYPT=$ISENCRYPTED
# Although this most certainly won't work without root, keep it just in case # Although this most certainly won't work without root, keep it just in case
if [ -e /dev/block/by-name/vbmeta_a ] || [ -e /dev/block/by-name/vbmeta ]; then if [ -e /dev/block/by-name/vbmeta_a ] || [ -e /dev/block/by-name/vbmeta ]; then
@@ -204,7 +206,8 @@ get_flags() {
fi fi
# Preset PATCHVBMETAFLAG to false in the non-root case # Preset PATCHVBMETAFLAG to false in the non-root case
PATCHVBMETAFLAG=false PATCHVBMETAFLAG=false
# Do NOT preset RECOVERYMODE here # Make sure RECOVERYMODE has value
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
} }
run_migrations() { return; } run_migrations() { return; }
@@ -217,13 +220,12 @@ grep_prop() { return; }
app_init() { app_init() {
mount_partitions mount_partitions
RAMDISKEXIST=false
check_boot_ramdisk && RAMDISKEXIST=true
get_flags get_flags
run_migrations run_migrations
SHA1=$(grep_prop SHA1 $MAGISKTMP/config) SHA1=$(grep_prop SHA1 $MAGISKTMP/config)
check_boot_ramdisk && RAMDISKEXIST=true || RAMDISKEXIST=false
check_encryption check_encryption
# Make sure RECOVERYMODE has value
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
} }
export BOOTMODE=true export BOOTMODE=true

View File

@@ -13,7 +13,7 @@
<string name="no_connection">L\'accesu a internet nun ta disponible</string> <string name="no_connection">L\'accesu a internet nun ta disponible</string>
<string name="app_changelog">Rexistru de cambeos</string> <string name="app_changelog">Rexistru de cambeos</string>
<string name="loading">Cargando…</string> <string name="loading">Cargando…</string>
<string name="update">Anovamienu</string> <string name="update">Anovar</string>
<string name="not_available">N/D</string> <string name="not_available">N/D</string>
<string name="hide">Anubrir</string> <string name="hide">Anubrir</string>
<string name="home_package">Paquete</string> <string name="home_package">Paquete</string>
@@ -23,8 +23,8 @@
<string name="home_follow_title">Redes sociales</string> <string name="home_follow_title">Redes sociales</string>
<string name="home_item_source">Códigu fonte</string> <string name="home_item_source">Códigu fonte</string>
<string name="home_support_content">Magisk ye y va ser de códigu abiertu y gratuitu. Sicasí, pues ayudanos faciendo una donación o collaborando.</string> <string name="home_support_content">Magisk ye y va ser de códigu abiertu y gratuitu. Sicasí, pues ayudanos faciendo una donación o collaborando.</string>
<string name="home_installed_version">V. instalada</string> <string name="home_installed_version">Versión instalada</string>
<string name="home_latest_version">Última v.</string> <string name="home_latest_version">Última versión</string>
<string name="invalid_update_channel">La canal d\'anovamientu nun ye válida</string> <string name="invalid_update_channel">La canal d\'anovamientu nun ye válida</string>
<string name="uninstall_magisk_title">Desinstalar Magisk</string> <string name="uninstall_magisk_title">Desinstalar Magisk</string>
<string name="uninstall_magisk_msg">¡Van quitase y desactivase tolos módulos y l\'accesu root!\nCualesquier almacenamientu internu ensin cifrar pente l\'usu de Magisk va volver cifrase.</string> <string name="uninstall_magisk_msg">¡Van quitase y desactivase tolos módulos y l\'accesu root!\nCualesquier almacenamientu internu ensin cifrar pente l\'usu de Magisk va volver cifrase.</string>
@@ -37,13 +37,13 @@
<string name="install_method_title">Métodu</string> <string name="install_method_title">Métodu</string>
<string name="install_next">Siguiente</string> <string name="install_next">Siguiente</string>
<string name="install_start">Siguir</string> <string name="install_start">Siguir</string>
<string name="manager_download_install">Primi pa baxar ya instalar</string> <string name="manager_download_install">Primi equí pa baxalu ya instalalu</string>
<string name="direct_install">Instalación direuta (aconséyase)</string> <string name="direct_install">Instalación direuta (aconséyase)</string>
<string name="install_inactive_slot">Instalar na ralura inactiva (darréu del OTA)</string> <string name="install_inactive_slot">Instalar na ralura inactiva (darréu del OTA)</string>
<string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva darréu de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string> <string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva darréu de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string>
<string name="setup_title">Configuración adicional</string> <string name="setup_title">Configuración adicional</string>
<string name="select_patch_file">Esbillar y parchiar un ficheru</string> <string name="select_patch_file">Seleicionar y parchiar un ficheru</string>
<string name="patch_file_msg">Esbilla una imaxe en bruto (*.img) o un archivu d\'ODIN (*.tar)</string> <string name="patch_file_msg">Seleiciona una imaxe en bruto (*.img) o un archivu d\'ODIN (*.tar)</string>
<string name="reboot_delay_toast">Reaniciando en 5 segundos…</string> <string name="reboot_delay_toast">Reaniciando en 5 segundos…</string>
<string name="flash_screen_title">Instalación</string> <string name="flash_screen_title">Instalación</string>
<!--Superuser--> <!--Superuser-->
@@ -106,7 +106,7 @@
<string name="module_empty">Nun hai nengún módulu instaláu</string> <string name="module_empty">Nun hai nengún módulu instaláu</string>
<!--Settings--> <!--Settings-->
<string name="settings_dark_mode_title">Mou del estilu</string> <string name="settings_dark_mode_title">Mou del estilu</string>
<string name="settings_dark_mode_message">¡Esbilla\'l mou que meyor s\'adaute al to estilu!</string> <string name="settings_dark_mode_message">¡Seleiciona\'l mou que meyor s\'adaute al to estilu!</string>
<string name="settings_dark_mode_light">Claridá</string> <string name="settings_dark_mode_light">Claridá</string>
<string name="settings_dark_mode_system">L\'estilu del sistema</string> <string name="settings_dark_mode_system">L\'estilu del sistema</string>
<string name="settings_dark_mode_dark">Escuridá</string> <string name="settings_dark_mode_dark">Escuridá</string>
@@ -130,7 +130,7 @@
<string name="settings_denylist_summary">Los procesos de la llista d\'esclusión tienen toles modificaciones de Magisk anulaes</string> <string name="settings_denylist_summary">Los procesos de la llista d\'esclusión tienen toles modificaciones de Magisk anulaes</string>
<string name="settings_denylist_error">Esta función rique l\'activación de: %1$s</string> <string name="settings_denylist_error">Esta función rique l\'activación de: %1$s</string>
<string name="settings_denylist_config_title">Configurar la llista d\'esclusión</string> <string name="settings_denylist_config_title">Configurar la llista d\'esclusión</string>
<string name="settings_denylist_config_summary">Esbilla los procesos que s\'inclúin na llista d\'esclusión</string> <string name="settings_denylist_config_summary">Seleiciona los procesos que s\'inclúin na llista d\'esclusión</string>
<string name="settings_hosts_title">Módulu «Systemless Hosts»</string> <string name="settings_hosts_title">Módulu «Systemless Hosts»</string>
<string name="settings_hosts_summary">Un módulu pa les aplicaciones que bloquien anuncios</string> <string name="settings_hosts_summary">Un módulu pa les aplicaciones que bloquien anuncios</string>
<string name="settings_hosts_toast">Amestóse\'l módulu «Systemless Hosts»</string> <string name="settings_hosts_toast">Amestóse\'l módulu «Systemless Hosts»</string>

View File

@@ -5,44 +5,49 @@
<string name="superuser">Superuser</string> <string name="superuser">Superuser</string>
<string name="logs">Log</string> <string name="logs">Log</string>
<string name="settings">Setelan</string> <string name="settings">Setelan</string>
<string name="install">Instal</string> <string name="install">Pasang</string>
<string name="section_home">Beranda</string> <string name="section_home">Beranda</string>
<string name="section_theme">Tema</string> <string name="section_theme">Tema</string>
<string name="denylist">DenyList</string>
<!--Home--> <!--Home-->
<string name="no_connection">Koneksi tidak tersedia</string> <string name="no_connection">Koneksi tidak tersedia</string>
<string name="app_changelog">Catatan perubahan</string> <string name="app_changelog">Catatan perubahan</string>
<string name="loading">Memuat…</string> <string name="loading">Memuat…</string>
<string name="update">Update</string> <string name="update">Perbarui</string>
<string name="not_available">N/A</string> <string name="not_available">N/A</string>
<string name="hide">Tutup</string> <string name="hide">Tutup</string>
<string name="home_package">Paket</string> <string name="home_package">Paket</string>
<string name="home_app_title">Aplikasi</string>
<string name="home_notice_content">Unduh Magisk HANYA dari halaman GitHub resmi kami. File dari sumber yang tidak dikenal bisa berbahaya!</string>
<string name="home_support_title">Dukung kami</string> <string name="home_support_title">Dukung kami</string>
<string name="home_follow_title">Ikuti Kami</string>
<string name="home_item_source">Sumber</string> <string name="home_item_source">Sumber</string>
<string name="home_support_content">Magisk gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string> <string name="home_support_content">Magisk gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string>
<string name="home_installed_version">Terinstal</string> <string name="home_installed_version">Terpasang</string>
<string name="home_latest_version">Terbaru</string> <string name="home_latest_version">Terbaru</string>
<string name="invalid_update_channel">Kanal update tidak valid</string> <string name="invalid_update_channel">Saluran pembaruan tidak valid</string>
<string name="uninstall_magisk_title">Uninstal Magisk</string> <string name="uninstall_magisk_title">Copot Magisk</string>
<string name="uninstall_magisk_msg">Semua modul akan dinonaktifkan/dihapus!\nRoot akan dihapus!\nData Anda berpotensi terenkripsi jika belum!</string> <string name="uninstall_magisk_msg">Semua modul akan dinonaktifkan/dihapus!\nRoot akan dihapus!\nData Anda berpotensi terenkripsi jika belum!</string>
<!--Install--> <!--Install-->
<string name="keep_force_encryption">Pertahankan enkripsi paksa</string> <string name="keep_force_encryption">Pertahankan enkripsi paksa</string>
<string name="keep_dm_verity">Pertahankan AVB 2.0/dm-verity</string> <string name="keep_dm_verity">Pertahankan AVB 2.0/dm-verity</string>
<string name="patch_vbmeta">Tambal vbmeta dalam boot image</string>
<string name="recovery_mode">Mode Recovery</string> <string name="recovery_mode">Mode Recovery</string>
<string name="install_options_title">Opsi</string> <string name="install_options_title">Opsi</string>
<string name="install_method_title">Metode</string> <string name="install_method_title">Metode</string>
<string name="install_next">Berikutnya</string> <string name="install_next">Berikutnya</string>
<string name="install_start">Mulai</string> <string name="install_start">Mulai</string>
<string name="manager_download_install">Sentuh untuk download dan instal</string> <string name="manager_download_install">Sentuh untuk unduh dan pasang</string>
<string name="direct_install">Langsung instal (Disarankan)</string> <string name="direct_install">Langsung pasang (Disarankan)</string>
<string name="install_inactive_slot">Instal pada slot nonaktif (Setelah OTA)</string> <string name="install_inactive_slot">Pasang pada slot nonaktif (Setelah OTA)</string>
<string name="install_inactive_slot_msg">Perangkat Anda akan DIPAKSA boot ke slot yang saat ini tidak aktif setelah perangkat dinyalakan ulang!\nGunakan opsi ini hanya setelah proses OTA selesai.\nLanjutkan?</string> <string name="install_inactive_slot_msg">Perangkat Anda akan DIPAKSA boot ke slot yang saat ini tidak aktif setelah perangkat dinyalakan ulang!\nGunakan opsi ini hanya setelah proses OTA selesai.\nLanjutkan?</string>
<string name="setup_title">Penyiapan tambahan</string> <string name="setup_title">Penyiapan tambahan</string>
<string name="select_patch_file">Pilih dan tambal file</string> <string name="select_patch_file">Pilih dan tambal file</string>
<string name="patch_file_msg">Pilih mentahan image (*.img) atau file tar ODIN (*.tar)</string> <string name="patch_file_msg">Pilih mentahan image (*.img) atau file tar ODIN (*.tar)</string>
<string name="reboot_delay_toast">Memulai kembali dalam 5 detik…</string> <string name="reboot_delay_toast">Memulai ulang dalam 5 detik…</string>
<string name="flash_screen_title">Instalasi</string> <string name="flash_screen_title">Instalasi</string>
<!--Superuser--> <!--Superuser-->
@@ -94,16 +99,20 @@
<!--Module--> <!--Module-->
<string name="no_info_provided">(Info tidak tersedia)</string> <string name="no_info_provided">(Info tidak tersedia)</string>
<string name="reboot_userspace">Nyalakan ulang secara halus</string> <string name="reboot_userspace">Mulai ulang secara halus</string>
<string name="reboot_recovery">Nyalakan ke mode Recovery</string> <string name="reboot_recovery">Mulai ulang ke mode Recovery</string>
<string name="reboot_bootloader">Nyalakan ke mode Bootloader</string> <string name="reboot_bootloader">Mulai ulang ke mode Bootloader</string>
<string name="reboot_download">Nyalakan ke mode Download</string> <string name="reboot_download">Mulai ulang ke mode Download</string>
<string name="reboot_edl">Nyalakan ke mode EDL</string> <string name="reboot_edl">Mulai ulang ke mode EDL</string>
<string name="module_version_author">%1$s oleh %2$s</string> <string name="module_version_author">%1$s oleh %2$s</string>
<string name="module_state_remove">Hapus</string> <string name="module_state_remove">Hapus</string>
<string name="module_state_restore">Pulihkan</string> <string name="module_state_restore">Pulihkan</string>
<string name="module_action_install_external">Instal dari penyimpanan</string> <string name="module_action_install_external">Pasang dari penyimpanan</string>
<string name="update_available">Update tersedia</string> <string name="update_available">Pembaruan tersedia</string>
<string name="suspend_text_riru">Modul ditangguhkan karena %1$s diaktifkan</string>
<string name="suspend_text_zygisk">Modul ditangguhkan karena %1$s tidak diaktifkan</string>
<string name="zygisk_module_unloaded">Modul Zygisk tidak dimuat karena ketidakcocokan</string>
<string name="module_empty">Tidak ada modul terpasang</string>
<!--Settings--> <!--Settings-->
<string name="settings_dark_mode_title">Mode tema</string> <string name="settings_dark_mode_title">Mode tema</string>
@@ -111,17 +120,27 @@
<string name="settings_dark_mode_light">Selalu terang</string> <string name="settings_dark_mode_light">Selalu terang</string>
<string name="settings_dark_mode_system">Ikuti sistem</string> <string name="settings_dark_mode_system">Ikuti sistem</string>
<string name="settings_dark_mode_dark">Selalu gelap</string> <string name="settings_dark_mode_dark">Selalu gelap</string>
<string name="settings_download_path_title">Lokasi download</string> <string name="settings_download_path_title">Lokasi unduhan</string>
<string name="settings_download_path_message">File akan disimpan ke %1$s</string> <string name="settings_download_path_message">File akan disimpan ke %1$s</string>
<string name="settings_hide_app_title">Sembunyikan aplikasi Magisk</string>
<string name="settings_hide_app_summary">Pasang aplikasi proxy dengan ID paket acak dan label aplikasi khusus</string>
<string name="settings_restore_app_title">Pulihkan aplikasi Magisk</string>
<string name="settings_restore_app_summary">Tampilkan aplikasi and pulihkan APK asli</string>
<string name="language">Bahasa</string> <string name="language">Bahasa</string>
<string name="system_default">(Default sistem)</string> <string name="system_default">(Bawaan sistem)</string>
<string name="settings_check_update_title">Periksa update</string> <string name="settings_check_update_title">Periksa pembaruan</string>
<string name="settings_check_update_summary">Periksa update secara berkala di latar belakang</string> <string name="settings_check_update_summary">Periksa pembaruan secara berkala di latar belakang</string>
<string name="settings_update_channel_title">Kanal update</string> <string name="settings_update_channel_title">Saluran pembaruan</string>
<string name="settings_update_stable">Stabil</string> <string name="settings_update_stable">Stabil</string>
<string name="settings_update_beta">Beta</string> <string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Kanal khusus</string> <string name="settings_update_custom">Saluran khusus</string>
<string name="settings_update_custom_msg">Masukkan URL khusus</string> <string name="settings_update_custom_msg">Masukkan URL khusus</string>
<string name="settings_zygisk_summary">Jalankan bagian-bagian Magisk dalam zygote daemon</string>
<string name="settings_denylist_title">Paksa DenyList</string>
<string name="settings_denylist_summary">Proses pada denylist akan mengembalikan semua modifikasi Magisk</string>
<string name="settings_denylist_error">Fitur ini membutuhkan %1$s untuk diaktifkan</string>
<string name="settings_denylist_config_title">Konfigurasi DenyList</string>
<string name="settings_denylist_config_summary">Pilih proses yang akan disertakan pada denylist</string>
<string name="settings_hosts_title">Host systemless</string> <string name="settings_hosts_title">Host systemless</string>
<string name="settings_hosts_summary">Dukungan host secara systemless untuk aplikasi pemblokir iklan</string> <string name="settings_hosts_summary">Dukungan host secara systemless untuk aplikasi pemblokir iklan</string>
<string name="settings_hosts_toast">Menambahkan modul host systemless</string> <string name="settings_hosts_toast">Menambahkan modul host systemless</string>
@@ -142,8 +161,8 @@
<string name="auto_response">Respons otomatis</string> <string name="auto_response">Respons otomatis</string>
<string name="request_timeout">Batas waktu permintaan</string> <string name="request_timeout">Batas waktu permintaan</string>
<string name="superuser_notification">Notifikasi superuser</string> <string name="superuser_notification">Notifikasi superuser</string>
<string name="settings_su_reauth_title">Autentikasi ulang setelah upgrade</string> <string name="settings_su_reauth_title">Autentikasi ulang setelah peningkatan</string>
<string name="settings_su_reauth_summary">Autentikasi ulang izin akses superuser setelah aplikasi diupgrade</string> <string name="settings_su_reauth_summary">Autentikasi ulang izin akses superuser setelah aplikasi ditingkatkan</string>
<string name="settings_su_tapjack_title">Aktifkan perlindungan tapjacking</string> <string name="settings_su_tapjack_title">Aktifkan perlindungan tapjacking</string>
<string name="settings_su_tapjack_summary">Dialog permintaan superuser tidak akan menanggapi masukan saat terhalangi oleh lapisan atau jendela lainnya</string> <string name="settings_su_tapjack_summary">Dialog permintaan superuser tidak akan menanggapi masukan saat terhalangi oleh lapisan atau jendela lainnya</string>
<string name="settings_su_biometric_title">Aktifkan autentikasi biometrik</string> <string name="settings_su_biometric_title">Aktifkan autentikasi biometrik</string>
@@ -171,35 +190,51 @@
<string name="isolate_summary">Setiap sesi root akan memiliki ruang-nama tersendiri</string> <string name="isolate_summary">Setiap sesi root akan memiliki ruang-nama tersendiri</string>
<!--Notifications--> <!--Notifications-->
<string name="update_channel">Update Magisk</string> <string name="update_channel">Pembaruan Magisk</string>
<string name="progress_channel">Notifikasi Kemajuan</string> <string name="progress_channel">Notifikasi Kemajuan</string>
<string name="download_complete">Download selesai</string> <string name="updated_channel">Pembaruan Selesai</string>
<string name="download_file_error">Kesalahan saat mendownload file</string> <string name="download_complete">Unduhan selesai</string>
<string name="magisk_update_title">Update Magisk tersedia!</string> <string name="download_file_error">Kesalahan saat mengunduh file</string>
<string name="magisk_update_title">Pembaruan Magisk tersedia!</string>
<string name="updated_title">Magisk Diperbarui</string>
<string name="updated_text">Ketuk untuk buka aplikasi</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Ya</string> <string name="yes">Ya</string>
<string name="no">Tidak</string> <string name="no">Tidak</string>
<string name="repo_install_title">Instal %1$s %2$s(%3$d)</string> <string name="repo_install_title">Pasang %1$s %2$s(%3$d)</string>
<string name="download">Download</string> <string name="download">Unduh</string>
<string name="reboot">Nyalakan ulang</string> <string name="reboot">Mulai ulang</string>
<string name="release_notes">Catatan rilis</string> <string name="release_notes">Catatan rilis</string>
<string name="flashing">Memasang…</string> <string name="flashing">Memasang…</string>
<string name="done">Selesai!</string> <string name="done">Selesai!</string>
<string name="failure">Gagal!</string> <string name="failure">Gagal!</string>
<string name="hide_app_title">Menyembunyikan aplikasi Magisk…</string>
<string name="open_link_failed_toast">Tidak ditemukan aplikasi untuk membuka link ini</string> <string name="open_link_failed_toast">Tidak ditemukan aplikasi untuk membuka link ini</string>
<string name="complete_uninstall">Uninstal penuh</string> <string name="complete_uninstall">Pencopotan penuh</string>
<string name="restore_img">Pulihkan image</string> <string name="restore_img">Pulihkan image</string>
<string name="restore_img_msg">Memulihkan…</string> <string name="restore_img_msg">Memulihkan…</string>
<string name="restore_done">Pemulihan selesai!</string> <string name="restore_done">Pemulihan selesai!</string>
<string name="restore_fail">Cadangan stock tidak ada!</string> <string name="restore_fail">Cadangan stock tidak ada!</string>
<string name="setup_fail">Penyiapan gagal</string> <string name="setup_fail">Penyiapan gagal</string>
<string name="env_fix_title">Perlu penyiapan tambahan</string> <string name="env_fix_title">Perlu penyiapan tambahan</string>
<string name="env_fix_msg">Perangkat Anda membutuhkan pengaturan tambahan untuk Magisk agar berfungsi dengan benar. Apakah Anda ingin melanjutkan dan Menyalakan ulang?</string>
<string name="setup_msg">Memproses penyiapan lingkungan…</string> <string name="setup_msg">Memproses penyiapan lingkungan…</string>
<string name="authenticate">Autentikasi</string> <string name="authenticate">Autentikasi</string>
<string name="unsupport_magisk_title">Versi Magisk tidak didukung</string> <string name="unsupport_magisk_title">Versi Magisk tidak didukung</string>
<string name="unsupport_magisk_msg">Versi aplikasi ini tidak mendukung versi Magisk yang lebih rendah dari %1$s.\n\nAplikasi akan berperilaku seolah-olah tidak ada Magisk yang dipasang, harap tingkatkan Magisk sesegera mungkin.</string>
<string name="unsupport_general_title">Keadaan tidak normal</string>
<string name="unsupport_system_app_msg">Menjalankan aplikasi ini sebagai aplikasi sistem tidak didukung. Harap kembalikan aplikasi ke aplikasi pengguna.</string>
<string name="unsupport_other_su_msg">Sebuah \"su\" biner bukan dari Magisk telah terdeteksi. Hapus semua solusi root yang bersaing dan/atau pasang ulang Magisk.</string>
<string name="unsupport_external_storage_msg">Magisk dipasang ke penyimpanan eksternal. Harap pindahkan aplikasi ke penyimpanan internal.</string>
<string name="unsupport_nonroot_stub_msg">Aplikasi Magisk yang tersembunyi tidak dapat terus berfungsi karena root hilang. Tolong pulihkan APK asli.</string>
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
<string name="external_rw_permission_denied">Berikan izin akses ke penyimpanan untuk mengaktifkan fungsi ini</string> <string name="external_rw_permission_denied">Berikan izin akses ke penyimpanan untuk mengaktifkan fungsi ini</string>
<string name="install_unknown_denied">Izinkan "Sumber tidak dikenal" untuk mengaktifkan fungsi ini</string>
<string name="add_shortcut_title">Tambahkan pintasan ke layar utama</string> <string name="add_shortcut_title">Tambahkan pintasan ke layar utama</string>
<string name="add_shortcut_msg">Setelah menyembunyikan aplikasi ini, nama dan ikonnya mungkin sulit dikenali. Apakah Anda ingin menambahkan pintasan cantik ke layar utama?</string>
<string name="app_not_found">Tidak ditemukan aplikasi untuk menangani tindakan ini</string> <string name="app_not_found">Tidak ditemukan aplikasi untuk menangani tindakan ini</string>
<string name="reboot_apply_change">Mulai ulang untuk menerapkan perubahan</string>
<string name="restore_app_confirmation">Ini akan mengembalikan aplikasi tersembunyi kembali ke aplikasi asli. Apakah Anda benar-benar ingin melakukan ini?</string>
</resources> </resources>

View File

@@ -234,7 +234,7 @@
<string name="add_shortcut_title">Добавление ярлыка</string> <string name="add_shortcut_title">Добавление ярлыка</string>
<string name="add_shortcut_msg">После скрытия приложения Magisk его название и иконка могут быть неудобны для восприятия. Хотите создать ярлык на рабочем столе?</string> <string name="add_shortcut_msg">После скрытия приложения Magisk его название и иконка могут быть неудобны для восприятия. Хотите создать ярлык на рабочем столе?</string>
<string name="app_not_found">Приложение для обработки этого действия не найдено</string> <string name="app_not_found">Приложение для обработки этого действия не найдено</string>
<string name="reboot_apply_change">Перезагрузить для применения изменений</string> <string name="reboot_apply_change">Перезагрузите устройство для применения изменений</string>
<string name="restore_app_confirmation">Это действие восстановит пересобранное для скрытия приложение к исходному состоянию. Вы действительно хотите продолжить?</string> <string name="restore_app_confirmation">Это действие восстановит пересобранное для скрытия приложение к исходному состоянию. Вы действительно хотите продолжить?</string>
</resources> </resources>

View File

@@ -130,7 +130,7 @@
<string name="system_default">(Mặc định hệ thống)</string> <string name="system_default">(Mặc định hệ thống)</string>
<string name="settings_check_update_title">Kiểm tra cập nhật</string> <string name="settings_check_update_title">Kiểm tra cập nhật</string>
<string name="settings_check_update_summary">Kiểm tra định kỳ các bản cập nhật trong nền</string> <string name="settings_check_update_summary">Kiểm tra định kỳ các bản cập nhật trong nền</string>
<string name="settings_update_channel_title">Cập nhật kênh</string> <string name="settings_update_channel_title">Kênh cập nhật</string>
<string name="settings_update_stable">Ổn định</string> <string name="settings_update_stable">Ổn định</string>
<string name="settings_update_beta">Beta</string> <string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Kênh tùy chỉnh</string> <string name="settings_update_custom">Kênh tùy chỉnh</string>

Some files were not shown because too many files have changed in this diff Show More