Merge remote-tracking branch 'john/master' into feature/redesign

# Conflicts:
#	app/build.gradle
#	app/src/main/java/com/topjohnwu/magisk/ClassMap.kt
#	app/src/main/java/com/topjohnwu/magisk/Info.kt
#	app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt
#	app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt
#	app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt
#	app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt
This commit is contained in:
Viktor De Pasquale 2019-10-29 15:53:53 +01:00
commit f11bb609c9
190 changed files with 2204 additions and 1615 deletions

4
.gitignore vendored
View File

@ -2,8 +2,8 @@ out
*.zip *.zip
*.jks *.jks
*.apk *.apk
config.prop /config.prop
update.sh /update.sh
# Built binaries # Built binaries
native/out native/out

View File

@ -30,12 +30,12 @@ Furthermore, Magisk provides a **Systemless Interface** to alter the system (or
## Translations ## Translations
Default string resources for Magisk Manager are scattered throughout Default string resources for Magisk Manager and its stub APK are located here:
- `app/src/main/res/values/strings.xml` - `app/src/main/res/values/strings.xml`
- `shared/src/main/res/values/strings.xml` - `stub/src/main/res/values/strings.xml`
Translate each and place them in the respective locations (`<module>/src/main/res/values-<lang>/strings.xml`). Translate each and place them in the respective locations (`[module]/src/main/res/values-[lang]/strings.xml`).
## Signature Verification ## Signature Verification

View File

@ -75,7 +75,7 @@ dependencies {
implementation "${bindingAdapter}:${vBAdapt}" implementation "${bindingAdapter}:${vBAdapt}"
implementation "${bindingAdapter}-recyclerview:${vBAdapt}" implementation "${bindingAdapter}-recyclerview:${vBAdapt}"
def vMarkwon = '4.1.1' def vMarkwon = '4.1.2'
implementation "io.noties.markwon:core:${vMarkwon}" implementation "io.noties.markwon:core:${vMarkwon}"
implementation "io.noties.markwon:html:${vMarkwon}" implementation "io.noties.markwon:html:${vMarkwon}"
implementation "io.noties.markwon:image:${vMarkwon}" implementation "io.noties.markwon:image:${vMarkwon}"
@ -112,7 +112,7 @@ dependencies {
replacedBy('com.github.topjohnwu:room-runtime') replacedBy('com.github.topjohnwu:room-runtime')
} }
} }
def vRoom = "2.2.0" def vRoom = "2.2.1"
implementation "com.github.topjohnwu:room-runtime:${vRoom}" implementation "com.github.topjohnwu:room-runtime:${vRoom}"
kapt "androidx.room:room-compiler:${vRoom}" kapt "androidx.room:room-compiler:${vRoom}"
@ -124,9 +124,10 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03'
implementation 'androidx.browser:browser:1.0.0' implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01'
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc01'
implementation 'androidx.work:work-runtime:2.2.0' implementation 'androidx.work:work-runtime:2.2.0'
implementation 'androidx.transition:transition:1.2.0' implementation 'androidx.transition:transition:1.3.0-rc01'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.core:core-ktx:1.1.0'
implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'com.google.android.material:material:1.1.0-beta01'

View File

@ -32,7 +32,11 @@
-keep,allowobfuscation class * extends com.topjohnwu.magisk.base.DelegateWorker -keep,allowobfuscation class * extends com.topjohnwu.magisk.base.DelegateWorker
# BootSigner # BootSigner
-keepclassmembers class com.topjohnwu.signing.BootSigner { *; } -keep class a.a { *; }
# Workaround R8 bug
-keep,allowobfuscation class com.topjohnwu.magisk.model.receiver.GeneralReceiver
-keepclassmembers class a.e { *; }
# Strip logging # Strip logging
-assumenosideeffects class timber.log.Timber.Tree { *; } -assumenosideeffects class timber.log.Timber.Tree { *; }

5
app/res-ids.txt Normal file
View File

@ -0,0 +1,5 @@
com.topjohnwu.magisk:color/xxxxxxxx = 0x7f010000
com.topjohnwu.magisk:drawable/xxxxxxxx = 0x7f020000
com.topjohnwu.magisk:string/xxxxxxxx = 0x7f030000
com.topjohnwu.magisk:style/xxxxxxxx = 0x7f040000
com.topjohnwu.magisk:xml/xxxxxxxx = 0x7f050000

View File

@ -78,6 +78,7 @@
<activity <activity
android:name="a.m" android:name="a.m"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:directBootAware="true" android:directBootAware="true"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="false" /> android:exported="false" />
@ -88,6 +89,7 @@
android:name="a.h" android:name="a.h"
android:directBootAware="true"> android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.REBOOT" />
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter> </intent-filter>
@ -110,6 +112,12 @@
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
android:value="12451000" /> android:value="12451000" />
<!-- Initialize WorkManager on-demand -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application> </application>
</manifest> </manifest>

View File

@ -1,18 +1,21 @@
package a; package a;
import androidx.annotation.Keep;
import androidx.core.app.AppComponentFactory; import androidx.core.app.AppComponentFactory;
import com.topjohnwu.magisk.utils.PatchAPK; import com.topjohnwu.magisk.utils.PatchAPK;
import com.topjohnwu.signing.BootSigner; import com.topjohnwu.signing.BootSigner;
@Keep
public class a extends AppComponentFactory { public class a extends AppComponentFactory {
@Deprecated
public static boolean patchAPK(String in, String out, String pkg) { public static boolean patchAPK(String in, String out, String pkg) {
return PatchAPK.patch(in, out, pkg); return PatchAPK.patch(in, out, pkg);
} }
public static boolean patchAPK(String in, String out, String pkg, String label) {
return PatchAPK.patch(in, out, pkg, label);
}
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
BootSigner.main(args); BootSigner.main(args);
} }

View File

@ -3,5 +3,11 @@ package a;
import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.App;
public class e extends App { public class e extends App {
/* stub */ public e() {
super();
}
public e(Object o) {
super(o);
}
} }

View File

@ -7,7 +7,6 @@ import androidx.work.Worker;
import androidx.work.WorkerParameters; import androidx.work.WorkerParameters;
import com.topjohnwu.magisk.base.DelegateWorker; import com.topjohnwu.magisk.base.DelegateWorker;
import com.topjohnwu.magisk.utils.ResourceMgrKt;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -19,7 +18,7 @@ public abstract class w<T extends DelegateWorker> extends Worker {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
w(@NonNull Context context, @NonNull WorkerParameters workerParams) { w(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(ResourceMgrKt.wrap(context, false), workerParams); super(context, workerParams);
try { try {
base = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()) base = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]).newInstance(); .getActualTypeArguments()[0]).newInstance();

View File

@ -6,6 +6,7 @@ import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import androidx.room.Room import androidx.room.Room
import androidx.work.WorkManager
import androidx.work.impl.WorkDatabase import androidx.work.impl.WorkDatabase
import androidx.work.impl.WorkDatabase_Impl import androidx.work.impl.WorkDatabase_Impl
import com.topjohnwu.magisk.data.database.RepoDatabase import com.topjohnwu.magisk.data.database.RepoDatabase
@ -14,16 +15,17 @@ import com.topjohnwu.magisk.di.ActivityTracker
import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.extensions.unwrap
import com.topjohnwu.magisk.utils.ResourceMgr
import com.topjohnwu.magisk.utils.RootInit import com.topjohnwu.magisk.utils.RootInit
import com.topjohnwu.magisk.utils.isRunningAsStub
import com.topjohnwu.magisk.utils.wrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import timber.log.Timber import timber.log.Timber
open class App : Application() { open class App() : Application() {
constructor(o: Any) : this() {
Info.stub = DynAPK.load(o)
}
init { init {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
@ -50,7 +52,6 @@ open class App : Application() {
val app: Application val app: Application
val impl: Context val impl: Context
if (base is Application) { if (base is Application) {
isRunningAsStub = true
app = base app = base
impl = base.baseContext impl = base.baseContext
} else { } else {
@ -67,6 +68,7 @@ open class App : Application() {
} }
ResourceMgr.reload() ResourceMgr.reload()
app.registerActivityLifecycleCallbacks(get<ActivityTracker>()) app.registerActivityLifecycleCallbacks(get<ActivityTracker>())
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
} }
// This is required as some platforms expect ContextImpl // This is required as some platforms expect ContextImpl

View File

@ -1,29 +0,0 @@
package com.topjohnwu.magisk
import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.model.update.UpdateCheckService
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.SplashActivity
import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.redesign.MainActivity as RedesignActivity
object ClassMap {
private val map = mapOf(
App::class.java to a.e::class.java,
MainActivity::class.java to a.b::class.java,
SplashActivity::class.java to a.c::class.java,
FlashActivity::class.java to a.f::class.java,
UpdateCheckService::class.java to a.g::class.java,
GeneralReceiver::class.java to a.h::class.java,
DownloadService::class.java to a.j::class.java,
SuRequestActivity::class.java to a.m::class.java,
//redesign
RedesignActivity::class.java to a.i::class.java
)
operator fun <T : Class<*>>get(c: Class<*>): T {
return map.getOrElse(c) { throw IllegalArgumentException() } as T
}
}

View File

@ -33,8 +33,9 @@ object Config : PreferenceModel, DBConfig {
const val ROOT_ACCESS = "root_access" const val ROOT_ACCESS = "root_access"
const val SU_MULTIUSER_MODE = "multiuser_mode" const val SU_MULTIUSER_MODE = "multiuser_mode"
const val SU_MNT_NS = "mnt_ns" const val SU_MNT_NS = "mnt_ns"
const val SU_MANAGER = "requester"
const val SU_FINGERPRINT = "su_fingerprint" const val SU_FINGERPRINT = "su_fingerprint"
const val SU_MANAGER = "requester"
const val KEYSTORE = "keystore"
// prefs // prefs
const val SU_REQUEST_TIMEOUT = "su_request_timeout" const val SU_REQUEST_TIMEOUT = "su_request_timeout"
@ -100,7 +101,12 @@ object Config : PreferenceModel, DBConfig {
} }
private val defaultChannel = private val defaultChannel =
if (Utils.isCanary) Value.CANARY_DEBUG_CHANNEL if (Utils.isCanary) {
if (BuildConfig.DEBUG)
Value.CANARY_DEBUG_CHANNEL
else
Value.CANARY_CHANNEL
}
else Value.DEFAULT_CHANNEL else Value.DEFAULT_CHANNEL
var downloadPath by preference(Key.DOWNLOAD_PATH, Environment.DIRECTORY_DOWNLOADS) var downloadPath by preference(Key.DOWNLOAD_PATH, Environment.DIRECTORY_DOWNLOADS)
@ -133,6 +139,7 @@ object Config : PreferenceModel, DBConfig {
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY) var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
var suFingerprint by dbSettings(Key.SU_FINGERPRINT, false) var suFingerprint by dbSettings(Key.SU_FINGERPRINT, false)
var suManager by dbStrings(Key.SU_MANAGER, "", true) var suManager by dbStrings(Key.SU_MANAGER, "", true)
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
// Always return a path in external storage where we can write // Always return a path in external storage where we can write
val downloadDirectory get() = val downloadDirectory get() =
@ -141,9 +148,6 @@ object Config : PreferenceModel, DBConfig {
fun initialize() = prefs.edit { fun initialize() = prefs.edit {
parsePrefs(this) parsePrefs(this)
if (!prefs.contains(Key.UPDATE_CHANNEL))
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
// Get actual state // Get actual state
putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists()) putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
@ -152,6 +156,9 @@ object Config : PreferenceModel, DBConfig {
putString(Key.SU_MNT_NS, suMntNamespaceMode.toString()) putString(Key.SU_MNT_NS, suMntNamespaceMode.toString())
putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString()) putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString())
putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint()) putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint())
}.also {
if (!prefs.contains(Key.UPDATE_CHANNEL))
prefs.edit().putString(Key.UPDATE_CHANNEL, defaultChannel.toString()).apply()
} }
private fun parsePrefs(editor: SharedPreferences.Editor) = editor.apply { private fun parsePrefs(editor: SharedPreferences.Editor) = editor.apply {

View File

@ -22,8 +22,9 @@ object Const {
const val MANAGER_CONFIGS = ".tmp.magisk.config" const val MANAGER_CONFIGS = ".tmp.magisk.config"
val USER_ID = Process.myUid() / 100000 val USER_ID = Process.myUid() / 100000
object MagiskVersion { object Version {
const val MIN_SUPPORT = 18000 const val MIN_SUPPORT = 18000
const val CONNECT_MODE = 20002
} }
object ID { object ID {

View File

@ -0,0 +1,224 @@
@file:Suppress("DEPRECATION")
package com.topjohnwu.magisk
import android.annotation.SuppressLint
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.app.job.JobWorkItem
import android.content.ComponentName
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.res.AssetManager
import android.content.res.Configuration
import android.content.res.Resources
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import com.topjohnwu.magisk.extensions.langTagToLocale
import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.model.update.UpdateCheckService
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.SplashActivity
import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.magisk.utils.defaultLocale
import java.util.*
fun AssetManager.addAssetPath(path: String) {
DynAPK.addAssetPath(this, path)
}
fun Context.wrap(global: Boolean = true): Context
= if (global) GlobalResContext(this) else ResContext(this)
fun Context.wrapJob(): Context = object : GlobalResContext(this) {
override fun getApplicationContext(): Context {
return this
}
@SuppressLint("NewApi")
override fun getSystemService(name: String): Any? {
return if (!isRunningAsStub) super.getSystemService(name) else
when (name) {
Context.JOB_SCHEDULER_SERVICE ->
JobSchedulerWrapper(super.getSystemService(name) as JobScheduler)
else -> super.getSystemService(name)
}
}
}
// Override locale and inject resources from dynamic APK
private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources {
config.setLocale(currentLocale)
updateConfiguration(config, displayMetrics)
if (isRunningAsStub)
assets.addAssetPath(ResourceMgr.resApk)
return this
}
fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName {
val name = ClassMap[this].name
return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: name)
}
fun Context.intent(c: Class<*>): Intent {
val cls = ClassMap[c]
return Info.stub?.let {
val className = it.componentMap.getOrElse(cls.name) { cls.name }
Intent().setComponent(ComponentName(this, className))
} ?: Intent(this, cls)
}
fun resolveRes(idx: Int): Int {
return Info.stub?.resourceMap?.get(idx) ?: when(idx) {
DynAPK.NOTIFICATION -> R.drawable.ic_magisk_outline
DynAPK.DOWNLOAD -> R.drawable.sc_cloud_download
DynAPK.SUPERUSER -> R.drawable.sc_superuser
DynAPK.MODULES -> R.drawable.sc_extension
DynAPK.MAGISKHIDE -> R.drawable.sc_magiskhide
else -> -1
}
}
private open class GlobalResContext(base: Context) : ContextWrapper(base) {
open val mRes: Resources get() = ResourceMgr.resource
private val loader by lazy { javaClass.classLoader!! }
override fun getResources(): Resources {
return mRes
}
override fun getClassLoader(): ClassLoader {
return loader
}
override fun createConfigurationContext(config: Configuration): Context {
return ResContext(super.createConfigurationContext(config))
}
}
private class ResContext(base: Context) : GlobalResContext(base) {
override val mRes by lazy { base.resources.patch() }
}
object ResourceMgr {
internal lateinit var resource: Resources
internal lateinit var resApk: String
fun init(context: Context) {
resource = context.resources
if (isRunningAsStub)
resApk = DynAPK.current(context).path
}
fun reload(config: Configuration = Configuration(resource.configuration)) {
val localeConfig = Config.locale
currentLocale = when {
localeConfig.isEmpty() -> defaultLocale
else -> localeConfig.langTagToLocale()
}
Locale.setDefault(currentLocale)
resource.patch(config)
}
fun getString(locale: Locale, @StringRes id: Int): String {
val config = Configuration()
config.setLocale(locale)
return Resources(resource.assets, resource.displayMetrics, config).getString(id)
}
}
@RequiresApi(api = 28)
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
override fun schedule(job: JobInfo): Int {
return base.schedule(job.patch())
}
override fun enqueue(job: JobInfo, work: JobWorkItem): Int {
return base.enqueue(job.patch(), work)
}
override fun cancel(jobId: Int) {
base.cancel(jobId)
}
override fun cancelAll() {
base.cancelAll()
}
override fun getAllPendingJobs(): List<JobInfo> {
return base.allPendingJobs
}
override fun getPendingJob(jobId: Int): JobInfo? {
return base.getPendingJob(jobId)
}
private fun JobInfo.patch(): JobInfo {
// We need to patch the component of JobInfo to access WorkManager SystemJobService
val name = service.className
val component = ComponentName(
service.packageName,
Info.stub!!.componentMap[name] ?: name)
// Clone the JobInfo except component
val builder = JobInfo.Builder(id, component)
.setExtras(extras)
.setTransientExtras(transientExtras)
.setClipData(clipData, clipGrantFlags)
.setRequiredNetwork(requiredNetwork)
.setEstimatedNetworkBytes(estimatedNetworkDownloadBytes, estimatedNetworkUploadBytes)
.setRequiresCharging(isRequireCharging)
.setRequiresDeviceIdle(isRequireDeviceIdle)
.setRequiresBatteryNotLow(isRequireBatteryNotLow)
.setRequiresStorageNotLow(isRequireStorageNotLow)
.also {
triggerContentUris?.let { uris ->
for (uri in uris)
it.addTriggerContentUri(uri)
}
}
.setTriggerContentUpdateDelay(triggerContentUpdateDelay)
.setTriggerContentMaxDelay(triggerContentMaxDelay)
.setImportantWhileForeground(isImportantWhileForeground)
.setPrefetch(isPrefetch)
.setPersisted(isPersisted)
if (isPeriodic) {
builder.setPeriodic(intervalMillis, flexMillis)
} else {
if (minLatencyMillis > 0)
builder.setMinimumLatency(minLatencyMillis)
if (maxExecutionDelayMillis > 0)
builder.setOverrideDeadline(maxExecutionDelayMillis)
}
if (!isRequireDeviceIdle)
builder.setBackoffCriteria(initialBackoffMillis, backoffPolicy)
return builder.build()
}
}
object ClassMap {
private val map = mapOf(
App::class.java to a.e::class.java,
MainActivity::class.java to a.b::class.java,
SplashActivity::class.java to a.c::class.java,
FlashActivity::class.java to a.f::class.java,
UpdateCheckService::class.java to a.g::class.java,
GeneralReceiver::class.java to a.h::class.java,
DownloadService::class.java to a.j::class.java,
SuRequestActivity::class.java to a.m::class.java
)
operator fun get(c: Class<*>) = map.getOrElse(c) { throw IllegalArgumentException() }
}

View File

@ -1,18 +1,24 @@
package com.topjohnwu.magisk package com.topjohnwu.magisk
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.entity.UpdateInfo import com.topjohnwu.magisk.model.entity.UpdateInfo
import com.topjohnwu.magisk.utils.CachedValue
import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
val isRunningAsStub get() = Info.stub != null
object Info { object Info {
@JvmStatic val envRef = CachedValue { loadState() }
var magiskVersionCode = -1
@JvmStatic @JvmStatic
var magiskVersionString = "" val env by envRef // Local
var remote = UpdateInfo() // Remote
var remote = UpdateInfo() var stub: DynAPK.Data? = null // Stub
@JvmStatic @JvmStatic
var keepVerity = false var keepVerity = false
@ -21,11 +27,40 @@ object Info {
@JvmStatic @JvmStatic
var recovery = false var recovery = false
fun loadMagiskInfo() { val isConnected by lazy {
runCatching { KObservableField(false).also { field ->
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] ReactiveNetwork.observeNetworkConnectivity(get())
magiskVersionCode = ShellUtils.fastCmd("magisk -V").toInt() .subscribeK {
Config.magiskHide = Shell.su("magiskhide --status").exec().isSuccess field.value = it.available()
}
}
}
private fun loadState() = runCatching {
val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0]
val code = ShellUtils.fastCmd("magisk -V").toInt()
val hide = Shell.su("magiskhide --status").exec().isSuccess
var mode = -1
if (code >= Const.Version.CONNECT_MODE) {
mode = Shell.su("magisk --connect-mode").exec().code
if (mode == 0) {
// Manually trigger broadcast test
Shell.su("magisk --broadcast-test").exec()
}
}
Env(code, str, hide, mode)
}.getOrElse { Env() }
class Env(
val magiskVersionCode: Int = -1,
val magiskVersionString: String = "",
hide: Boolean = false,
var connectionMode: Int = -1
) {
val magiskHide get() = Config.magiskHide
init {
Config.magiskHide = hide
} }
} }
} }

View File

@ -21,7 +21,7 @@ import com.topjohnwu.magisk.extensions.set
import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.events.EventHandler
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.wrap
import kotlin.random.Random import kotlin.random.Random
typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit

View File

@ -4,7 +4,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.wrap
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
abstract class BaseReceiver : BroadcastReceiver(), KoinComponent { abstract class BaseReceiver : BroadcastReceiver(), KoinComponent {

View File

@ -2,7 +2,7 @@ package com.topjohnwu.magisk.base
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import com.topjohnwu.magisk.utils.wrap import com.topjohnwu.magisk.wrap
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
abstract class BaseService : Service(), KoinComponent { abstract class BaseService : Service(), KoinComponent {

View File

@ -1,28 +1,24 @@
package com.topjohnwu.magisk.base.viewmodel package com.topjohnwu.magisk.base.viewmodel
import android.app.Activity import android.app.Activity
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.topjohnwu.magisk.extensions.doOnSubscribeUi import com.topjohnwu.magisk.extensions.doOnSubscribeUi
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.events.BackPressEvent import com.topjohnwu.magisk.model.events.BackPressEvent
import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.PublishSubject
import com.topjohnwu.magisk.Info.isConnected as gIsConnected
abstract class BaseViewModel( abstract class BaseViewModel(
initialState: State = State.LOADING initialState: State = State.LOADING
) : LoadingViewModel(initialState) { ) : LoadingViewModel(initialState) {
val isConnected = KObservableField(false) val isConnected = object : KObservableField<Boolean>(gIsConnected.value, gIsConnected) {
override fun get(): Boolean {
init { return gIsConnected.value
ReactiveNetwork.observeNetworkConnectivity(get()) }
.subscribeK { isConnected.value = it.available() }
.add()
} }
fun withView(action: Activity.() -> Unit) { fun withView(action: Activity.() -> Unit) {

View File

@ -29,7 +29,7 @@ class MagiskRepository(
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException()
}.flatMap { }.flatMap {
// If remote version is lower than current installed, try switching to beta // If remote version is lower than current installed, try switching to beta
if (it.magisk.versionCode < Info.magiskVersionCode if (it.magisk.versionCode < Info.env.magiskVersionCode
&& Config.updateChannel == Config.Value.DEFAULT_CHANNEL) { && Config.updateChannel == Config.Value.DEFAULT_CHANNEL) {
Config.updateChannel = Config.Value.BETA_CHANNEL Config.updateChannel = Config.Value.BETA_CHANNEL
apiRaw.fetchBetaUpdate() apiRaw.fetchBetaUpdate()

View File

@ -20,15 +20,18 @@ import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.FileProvider
import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.DynamicClassLoader
import com.topjohnwu.magisk.utils.FileProvider
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.Shell
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.lang.reflect.Array as JArray
val packageName: String get() = get<Context>().packageName val packageName: String get() = get<Context>().packageName
@ -99,33 +102,38 @@ fun Context.readUri(uri: Uri) =
fun Intent.startActivity(context: Context) = context.startActivity(this) fun Intent.startActivity(context: Context) = context.startActivity(this)
fun Intent.toCommand(args: MutableList<String>) { fun Intent.startActivityWithRoot() {
if (action != null) { val args = mutableListOf("am", "start", "--user", Const.USER_ID.toString())
val cmd = toCommand(args).joinToString(" ")
Shell.su(cmd).submit()
}
fun Intent.toCommand(args: MutableList<String> = mutableListOf()): MutableList<String> {
action?.also {
args.add("-a") args.add("-a")
args.add(action!!) args.add(it)
} }
if (component != null) { component?.also {
args.add("-n") args.add("-n")
args.add(component!!.flattenToString()) args.add(it.flattenToString())
} }
if (data != null) { data?.also {
args.add("-d") args.add("-d")
args.add(dataString!!) args.add(it.toString())
} }
if (categories != null) { categories?.also {
for (cat in categories) { for (cat in it) {
args.add("-c") args.add("-c")
args.add(cat) args.add(cat)
} }
} }
if (type != null) { type?.also {
args.add("-t") args.add("-t")
args.add(type!!) args.add(it)
} }
val extras = extras extras?.also {
if (extras != null) { loop@ for (key in it.keySet()) {
loop@ for (key in extras.keySet()) { val v = it[key] ?: continue
val v = extras.get(key) ?: continue
var value: Any = v var value: Any = v
val arg: String val arg: String
when { when {
@ -139,9 +147,8 @@ fun Intent.toCommand(args: MutableList<String>) {
arg = "--ecn" arg = "--ecn"
value = v.flattenToString() value = v.flattenToString()
} }
v is ArrayList<*> -> { v is List<*> -> {
if (v.size <= 0) if (v.isEmpty())
/* Impossible to know the type due to type erasure */
continue@loop continue@loop
arg = if (v[0] is Int) arg = if (v[0] is Int)
@ -177,11 +184,9 @@ fun Intent.toCommand(args: MutableList<String>) {
continue@loop /* Unsupported */ continue@loop /* Unsupported */
val sb = StringBuilder() val sb = StringBuilder()
val len = java.lang.reflect.Array.getLength(v) val len = JArray.getLength(v)
for (i in 0 until len) { for (i in 0 until len) {
sb.append( sb.append(JArray.get(v, i)!!.toString().replace(",", "\\,"))
java.lang.reflect.Array.get(v, i)!!.toString().replace(",", "\\,")
)
sb.append(',') sb.append(',')
} }
// Remove trailing comma // Remove trailing comma
@ -198,6 +203,7 @@ fun Intent.toCommand(args: MutableList<String>) {
} }
args.add("-f") args.add("-f")
args.add(flags.toString()) args.add(flags.toString())
return args
} }
fun File.provide(context: Context = get()): Uri { fun File.provide(context: Context = get()): Uri {

View File

@ -33,3 +33,5 @@ fun String.trimEmptyToNull(): String? = if (isBlank()) null else this
fun String.legalFilename() = replace(" ", "_").replace("'", "").replace("\"", "") fun String.legalFilename() = replace(" ", "_").replace("'", "").replace("\"", "")
.replace("$", "").replace("`", "").replace("*", "").replace("/", "_") .replace("$", "").replace("`", "").replace("*", "").replace("/", "_")
.replace("#", "").replace("@", "").replace("\\", "_") .replace("#", "").replace("@", "").replace("\\", "_")
fun String.isEmptyInternal() = isNullOrBlank()

View File

@ -7,11 +7,11 @@ import android.content.Intent
import android.os.Build import android.os.Build
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import com.topjohnwu.magisk.ClassMap
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.chooser import com.topjohnwu.magisk.extensions.chooser
import com.topjohnwu.magisk.extensions.exists import com.topjohnwu.magisk.extensions.exists
import com.topjohnwu.magisk.extensions.provide import com.topjohnwu.magisk.extensions.provide
import com.topjohnwu.magisk.intent
import com.topjohnwu.magisk.model.entity.internal.Configuration.* import com.topjohnwu.magisk.model.entity.internal.Configuration.*
import com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary import com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
@ -63,7 +63,7 @@ open class DownloadService : RemoteFileService() {
remove(id) remove(id)
when (subject.configuration) { when (subject.configuration) {
is APK.Upgrade -> APKInstall.install(this, subject.file) is APK.Upgrade -> APKInstall.install(this, subject.file)
else -> Unit is APK.Restore -> Unit
} }
} }
@ -140,8 +140,7 @@ open class DownloadService : RemoteFileService() {
inline operator fun invoke(context: Context, argBuilder: Builder.() -> Unit) { inline operator fun invoke(context: Context, argBuilder: Builder.() -> Unit) {
val app = context.applicationContext val app = context.applicationContext
val builder = Builder().apply(argBuilder) val builder = Builder().apply(argBuilder)
val intent = Intent(app, ClassMap[DownloadService::class.java]) val intent = app.intent(DownloadService::class.java).putExtra(ARG_URL, builder.subject)
.putExtra(ARG_URL, builder.subject)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
app.startForegroundService(intent) app.startForegroundService(intent)

View File

@ -1,23 +1,18 @@
package com.topjohnwu.magisk.model.download package com.topjohnwu.magisk.model.download
import android.content.ComponentName import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.ClassMap
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.DynamicClassLoader
import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Restore import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Restore
import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Upgrade import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Upgrade
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.ui.SplashActivity
import com.topjohnwu.magisk.utils.PatchAPK import com.topjohnwu.magisk.utils.PatchAPK
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import timber.log.Timber
import java.io.File import java.io.File
private fun RemoteFileService.patchPackage(apk: File, id: Int) { private fun RemoteFileService.patch(apk: File, id: Int) {
if (packageName != BuildConfig.APPLICATION_ID) { if (packageName == BuildConfig.APPLICATION_ID)
return
update(id) { notification -> update(id) { notification ->
notification.setProgress(0, 0, true) notification.setProgress(0, 0, true)
.setProgress(0, 0, true) .setProgress(0, 0, true)
@ -25,20 +20,29 @@ private fun RemoteFileService.patchPackage(apk: File, id: Int) {
.setContentText("") .setContentText("")
} }
val patched = File(apk.parent, "patched.apk") val patched = File(apk.parent, "patched.apk")
try { PatchAPK.patch(apk, patched, packageName, applicationInfo.nonLocalizedLabel.toString())
// Try using the new APK to patch itself
val loader = DynamicClassLoader(apk)
loader.loadClass("a.a")
.getMethod("patchAPK", String::class.java, String::class.java, String::class.java)
.invoke(null, apk.path, patched.path, packageName)
} catch (e: Exception) {
Timber.e(e)
// Fallback to use the current implementation
PatchAPK.patch(apk.path, patched.path, packageName)
}
apk.delete() apk.delete()
patched.renameTo(apk) patched.renameTo(apk)
} }
private fun RemoteFileService.upgrade(apk: File, id: Int) {
if (isRunningAsStub) {
// Move to upgrade location
apk.copyTo(DynAPK.update(this), overwrite = true)
apk.delete()
if (Info.stub!!.version < Info.remote.stub.versionCode) {
// We also want to upgrade stub
service.fetchFile(Info.remote.stub.link).blockingGet().byteStream().use {
it.writeTo(apk)
}
patch(apk, id)
} else {
// Simply relaunch the app
ProcessPhoenix.triggerRebirth(this)
}
} else {
patch(apk, id)
}
} }
private fun RemoteFileService.restore(apk: File, id: Int) { private fun RemoteFileService.restore(apk: File, id: Int) {
@ -51,15 +55,11 @@ private fun RemoteFileService.restore(apk: File, id: Int) {
Config.export() Config.export()
// Make it world readable // Make it world readable
apk.setReadable(true, false) apk.setReadable(true, false)
if (Shell.su("pm install $apk").exec().isSuccess) { Shell.su("pm install $apk && pm uninstall $packageName").exec()
val component = ComponentName(BuildConfig.APPLICATION_ID,
ClassMap.get<Class<*>>(SplashActivity::class.java).name)
Utils.rmAndLaunch(packageName, component)
}
} }
fun RemoteFileService.handleAPK(subject: DownloadSubject.Manager) fun RemoteFileService.handleAPK(subject: DownloadSubject.Manager)
= when (subject.configuration) { = when (subject.configuration) {
is Upgrade -> patchPackage(subject.file, subject.hashCode()) is Upgrade -> upgrade(subject.file, subject.hashCode())
is Restore -> restore(subject.file, subject.hashCode()) is Restore -> restore(subject.file, subject.hashCode())
} }

View File

@ -25,7 +25,7 @@ import java.io.InputStream
abstract class RemoteFileService : NotificationService() { abstract class RemoteFileService : NotificationService() {
private val service: GithubRawServices by inject() val service: GithubRawServices by inject()
override val defaultNotification: NotificationCompat.Builder override val defaultNotification: NotificationCompat.Builder
get() = Notifications.progress(this, "") get() = Notifications.progress(this, "")

View File

@ -8,7 +8,8 @@ import se.ansman.kotshi.JsonSerializable
data class UpdateInfo( data class UpdateInfo(
val app: ManagerJson = ManagerJson(), val app: ManagerJson = ManagerJson(),
val uninstaller: UninstallerJson = UninstallerJson(), val uninstaller: UninstallerJson = UninstallerJson(),
val magisk: MagiskJson = MagiskJson() val magisk: MagiskJson = MagiskJson(),
val stub: StubJson = StubJson()
) )
@JsonSerializable @JsonSerializable
@ -33,3 +34,9 @@ data class ManagerJson(
val link: String = "", val link: String = "",
val note: String = "" val note: String = ""
) : Parcelable ) : Parcelable
@JsonSerializable
data class StubJson(
val versionCode: Int = -1,
val link: String = ""
)

View File

@ -2,14 +2,14 @@ package com.topjohnwu.magisk.model.receiver
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import com.topjohnwu.magisk.ClassMap import android.os.Build.VERSION.SDK_INT
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.base.BaseReceiver import com.topjohnwu.magisk.base.BaseReceiver
import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.data.database.base.su import com.topjohnwu.magisk.data.database.base.su
import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.extensions.reboot
import com.topjohnwu.magisk.extensions.startActivity
import com.topjohnwu.magisk.extensions.startActivityWithRoot
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.entity.ManagerJson import com.topjohnwu.magisk.model.entity.ManagerJson
import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.Configuration
@ -20,6 +20,7 @@ import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.core.inject import org.koin.core.inject
import timber.log.Timber
open class GeneralReceiver : BaseReceiver() { open class GeneralReceiver : BaseReceiver() {
@ -38,6 +39,17 @@ open class GeneralReceiver : BaseReceiver() {
override fun onReceive(context: ContextWrapper, intent: Intent?) { override fun onReceive(context: ContextWrapper, intent: Intent?) {
intent ?: return intent ?: return
// Debug messages
if (BuildConfig.DEBUG) {
Timber.d(intent.action)
intent.extras?.let { bundle ->
bundle.keySet().forEach {
Timber.d("[%s]=[%s]", it, bundle[it])
}
}
}
when (intent.action ?: return) { when (intent.action ?: return) {
Intent.ACTION_REBOOT, Intent.ACTION_BOOT_COMPLETED -> { Intent.ACTION_REBOOT, Intent.ACTION_BOOT_COMPLETED -> {
val action = intent.getStringExtra("action") val action = intent.getStringExtra("action")
@ -51,16 +63,26 @@ open class GeneralReceiver : BaseReceiver() {
} }
when (action) { when (action) {
REQUEST -> { REQUEST -> {
val i = Intent(context, ClassMap[SuRequestActivity::class.java]) val i = context.intent(SuRequestActivity::class.java)
.setAction(action) .setAction(action)
.putExtra("socket", intent.getStringExtra("socket")) .putExtra("socket", intent.getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
context.startActivity(i) if (SDK_INT >= 29) {
// Android Q does not allow starting activity from background
i.startActivityWithRoot()
} else {
i.startActivity(context)
}
}
LOG -> SuLogger.handleLogs(context, intent)
NOTIFY -> SuLogger.handleNotify(context, intent)
TEST -> {
val mode = intent.getIntExtra("mode", 1 shl 1)
if (mode > Info.env.connectionMode)
Info.env.connectionMode = mode
Shell.su("magisk --connect-mode $mode").submit()
} }
LOG -> SuLogger.handleLogs(intent)
NOTIFY -> SuLogger.handleNotify(intent)
TEST -> Shell.su("magisk --use-broadcast").submit()
} }
} }
Intent.ACTION_PACKAGE_REPLACED -> Intent.ACTION_PACKAGE_REPLACED ->

View File

@ -20,7 +20,7 @@ class UpdateCheckService : DelegateWorker() {
magiskRepo.fetchUpdate().blockingGet() magiskRepo.fetchUpdate().blockingGet()
if (BuildConfig.VERSION_CODE < Info.remote.app.versionCode) if (BuildConfig.VERSION_CODE < Info.remote.app.versionCode)
Notifications.managerUpdate(applicationContext) Notifications.managerUpdate(applicationContext)
else if (Info.magiskVersionCode < Info.remote.magisk.versionCode) else if (Info.env.magiskVersionCode < Info.remote.magisk.versionCode)
Notifications.magiskUpdate(applicationContext) Notifications.magiskUpdate(applicationContext)
ListenableWorker.Result.success() ListenableWorker.Result.success()
}.getOrElse { }.getOrElse {

View File

@ -7,8 +7,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavController
import com.ncapdevi.fragnav.FragNavTransactionOptions import com.ncapdevi.fragnav.FragNavTransactionOptions
import com.topjohnwu.magisk.ClassMap
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const.Key.OPEN_SECTION import com.topjohnwu.magisk.Const.Key.OPEN_SECTION
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
@ -17,6 +15,7 @@ import com.topjohnwu.magisk.base.BaseFragment
import com.topjohnwu.magisk.databinding.ActivityMainBinding import com.topjohnwu.magisk.databinding.ActivityMainBinding
import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
import com.topjohnwu.magisk.extensions.snackbar import com.topjohnwu.magisk.extensions.snackbar
import com.topjohnwu.magisk.intent
import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder
import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
@ -61,7 +60,7 @@ open class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(), Na
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
if (!SplashActivity.DONE) { if (!SplashActivity.DONE) {
startActivity(Intent(this, ClassMap[SplashActivity::class.java])) startActivity(intent(SplashActivity::class.java))
finish() finish()
} }
@ -155,11 +154,11 @@ open class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(), Na
private fun checkHideSection() { private fun checkHideSection() {
val menu = binding.navView.menu val menu = binding.navView.menu
menu.findItem(R.id.magiskHideFragment).isVisible = menu.findItem(R.id.magiskHideFragment).isVisible =
Shell.rootAccess() && Config.magiskHide Shell.rootAccess() && Info.env.magiskHide
menu.findItem(R.id.modulesFragment).isVisible = menu.findItem(R.id.modulesFragment).isVisible =
Shell.rootAccess() && Info.magiskVersionCode >= 0 Shell.rootAccess() && Info.env.magiskVersionCode >= 0
menu.findItem(R.id.reposFragment).isVisible = menu.findItem(R.id.reposFragment).isVisible =
(viewModel.isConnected.value && Shell.rootAccess() && Info.magiskVersionCode >= 0) (viewModel.isConnected.value && Shell.rootAccess() && Info.env.magiskVersionCode >= 0)
menu.findItem(R.id.logFragment).isVisible = menu.findItem(R.id.logFragment).isVisible =
Shell.rootAccess() Shell.rootAccess()
menu.findItem(R.id.superuserFragment).isVisible = menu.findItem(R.id.superuserFragment).isVisible =

View File

@ -2,14 +2,12 @@ package com.topjohnwu.magisk.ui
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.wrap
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -24,11 +22,11 @@ open class SplashActivity : Activity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Shell.getShell { Shell.getShell {
if (Info.magiskVersionCode > 0 && Info.magiskVersionCode < Const.MagiskVersion.MIN_SUPPORT) { if (Info.env.magiskVersionCode > 0 && Info.env.magiskVersionCode < Const.Version.MIN_SUPPORT) {
AlertDialog.Builder(this) AlertDialog.Builder(this)
.setTitle(R.string.unsupport_magisk_title) .setTitle(R.string.unsupport_magisk_title)
.setMessage(R.string.unsupport_magisk_message) .setMessage(R.string.unsupport_magisk_message)
.setNegativeButton(R.string.ok, null) .setNegativeButton(android.R.string.ok, null)
.setOnDismissListener { finish() } .setOnDismissListener { finish() }
.show() .show()
} else { } else {

View File

@ -6,12 +6,12 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import com.topjohnwu.magisk.ClassMap
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.base.BaseActivity
import com.topjohnwu.magisk.databinding.ActivityFlashBinding import com.topjohnwu.magisk.databinding.ActivityFlashBinding
import com.topjohnwu.magisk.extensions.snackbar import com.topjohnwu.magisk.extensions.snackbar
import com.topjohnwu.magisk.intent
import com.topjohnwu.magisk.model.events.BackPressEvent import com.topjohnwu.magisk.model.events.BackPressEvent
import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
@ -60,7 +60,7 @@ open class FlashActivity : BaseActivity<FlashViewModel, ActivityFlashBinding>()
companion object { companion object {
private fun intent(context: Context) = Intent(context, ClassMap[FlashActivity::class.java]) private fun intent(context: Context) = context.intent(FlashActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
private fun intent(context: Context, file: File) = intent(context).setData(file.toUri()) private fun intent(context: Context, file: File) = intent(context).setData(file.toUri())

View File

@ -87,8 +87,8 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentMagiskBinding>(),
.setTitle(R.string.proprietary_title) .setTitle(R.string.proprietary_title)
.setMessage(R.string.proprietary_notice) .setMessage(R.string.proprietary_notice)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.yes) { _, _ -> download() } .setPositiveButton(android.R.string.yes) { _, _ -> download() }
.setNegativeButton(R.string.no_thanks) { _, _ -> viewModel.finishSafetyNetCheck(-2) } .setNegativeButton(android.R.string.no) { _, _ -> viewModel.finishSafetyNetCheck(-2) }
.show() .show()
} }

View File

@ -107,10 +107,10 @@ class HomeViewModel(
Info.recovery = it ?: return@addOnPropertyChangedCallback Info.recovery = it ?: return@addOnPropertyChangedCallback
} }
isConnected.addOnPropertyChangedCallback { isConnected.addOnPropertyChangedCallback {
if (it == true) refresh() if (it == true) refresh(false)
} }
refresh() refresh(false)
} }
fun paypalPressed() = OpenLinkEvent(Const.Url.PAYPAL_URL).publish() fun paypalPressed() = OpenLinkEvent(Const.Url.PAYPAL_URL).publish()
@ -170,7 +170,11 @@ class HomeViewModel(
} }
} }
fun refresh() { @JvmOverloads
fun refresh(invalidate: Boolean = true) {
if (invalidate)
Info.envRef.invalidate()
hasRoot.value = Shell.rootAccess() hasRoot.value = Shell.rootAccess()
val fetchUpdate = if (isConnected.value) val fetchUpdate = if (isConnected.value)
@ -179,7 +183,8 @@ class HomeViewModel(
Completable.complete() Completable.complete()
Completable.fromAction { Completable.fromAction {
Info.loadMagiskInfo() // Ensure value is ready
Info.env
}.andThen(fetchUpdate) }.andThen(fetchUpdate)
.applyViewModel(this) .applyViewModel(this)
.doOnSubscribeUi { .doOnSubscribeUi {
@ -197,33 +202,40 @@ class HomeViewModel(
private fun refreshVersions() { private fun refreshVersions() {
magiskCurrentVersion.value = if (magiskState.value != MagiskState.NOT_INSTALLED) { magiskCurrentVersion.value = if (magiskState.value != MagiskState.NOT_INSTALLED) {
version.format(Info.magiskVersionString, Info.magiskVersionCode) VERSION_FMT.format(Info.env.magiskVersionString, Info.env.magiskVersionCode)
} else { } else {
"" ""
} }
managerCurrentVersion.value = version managerCurrentVersion.value = if (isRunningAsStub) MGR_VER_FMT
.format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE) .format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, Info.stub!!.version)
else
VERSION_FMT.format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
} }
private fun updateSelf() { private fun updateSelf() {
magiskState.value = when (Info.magiskVersionCode) { magiskState.value = when (Info.env.magiskVersionCode) {
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED
!in Info.remote.magisk.versionCode..Int.MAX_VALUE -> MagiskState.OBSOLETE in 1 until (Info.remote.magisk.versionCode - 1) -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE else -> MagiskState.UP_TO_DATE
} }
magiskLatestVersion.value = version magiskLatestVersion.value =
.format(Info.remote.magisk.version, Info.remote.magisk.versionCode) VERSION_FMT.format(Info.remote.magisk.version, Info.remote.magisk.versionCode)
_managerState.value = when (Info.remote.app.versionCode) { _managerState.value = when (Info.remote.app.versionCode) {
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED //wrong update channel in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED //wrong update channel
in (BuildConfig.VERSION_CODE + 1)..Int.MAX_VALUE -> MagiskState.OBSOLETE in (BuildConfig.VERSION_CODE + 1) until Int.MAX_VALUE -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE else -> {
if (isRunningAsStub && Info.stub!!.version < Info.remote.stub.versionCode)
MagiskState.OBSOLETE
else
MagiskState.UP_TO_DATE
}
} }
managerLatestVersion.value = version managerLatestVersion.value = MGR_VER_FMT
.format(Info.remote.app.version, Info.remote.app.versionCode) .format(Info.remote.app.version, Info.remote.app.versionCode, Info.remote.stub.versionCode)
} }
private fun ensureEnv() { private fun ensureEnv() {
@ -240,7 +252,8 @@ class HomeViewModel(
} }
companion object { companion object {
private const val version = "%s (%d)" private const val VERSION_FMT = "%s (%d)"
private const val MGR_VER_FMT = "%s (%d) (%d)"
} }
} }

View File

@ -8,12 +8,12 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.topjohnwu.magisk.ClassMap
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.base.BaseFragment
import com.topjohnwu.magisk.databinding.FragmentModulesBinding import com.topjohnwu.magisk.databinding.FragmentModulesBinding
import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.extensions.reboot
import com.topjohnwu.magisk.intent
import com.topjohnwu.magisk.model.events.OpenFilePickerEvent import com.topjohnwu.magisk.model.events.OpenFilePickerEvent
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.flash.FlashActivity
@ -28,7 +28,7 @@ class ModulesFragment : BaseFragment<ModuleViewModel, FragmentModulesBinding>()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) { if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file // Get the URI of the selected file
val intent = Intent(activity, ClassMap[FlashActivity::class.java]) val intent = activity.intent(FlashActivity::class.java)
intent.setData(data.data).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP) intent.setData(data.data).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP)
startActivity(intent) startActivity(intent)
} }

View File

@ -13,20 +13,17 @@ import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BasePreferenceFragment import com.topjohnwu.magisk.base.BasePreferenceFragment
import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.data.database.RepoDao
import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding
import com.topjohnwu.magisk.databinding.DialogCustomNameBinding
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.toLangTag import com.topjohnwu.magisk.extensions.toLangTag
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.net.Networking
import com.topjohnwu.magisk.utils.* import com.topjohnwu.magisk.utils.*
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -58,6 +55,7 @@ class SettingsFragment : BasePreferenceFragment() {
findPreference<PreferenceCategory>("redesign_cat")?.isVisible = BuildConfig.DEBUG findPreference<PreferenceCategory>("redesign_cat")?.isVisible = BuildConfig.DEBUG
// Get preferences
updateChannel = findPreference(Config.Key.UPDATE_CHANNEL)!! updateChannel = findPreference(Config.Key.UPDATE_CHANNEL)!!
rootConfig = findPreference(Config.Key.ROOT_ACCESS)!! rootConfig = findPreference(Config.Key.ROOT_ACCESS)!!
autoRes = findPreference(Config.Key.SU_AUTO_RESPONSE)!! autoRes = findPreference(Config.Key.SU_AUTO_RESPONSE)!!
@ -71,17 +69,68 @@ class SettingsFragment : BasePreferenceFragment() {
val magiskCategory = findPreference<PreferenceCategory>("magisk")!! val magiskCategory = findPreference<PreferenceCategory>("magisk")!!
val suCategory = findPreference<PreferenceCategory>("superuser")!! val suCategory = findPreference<PreferenceCategory>("superuser")!!
val hideManager = findPreference<Preference>("hide")!! val hideManager = findPreference<Preference>("hide")!!
val restoreManager = findPreference<Preference>("restore")!!
// Remove/Disable entries
// Only show canary channels if user is already on canary channel
// or the user have already chosen canary channel
if (!Utils.isCanary && Config.updateChannel < Config.Value.CANARY_CHANNEL) {
// Remove the last 2 entries
val entries = updateChannel.entries
updateChannel.entries = entries.copyOf(entries.size - 2)
}
// Remove dangerous settings in secondary user
if (Const.USER_ID > 0) {
suCategory.removePreference(multiuserConfig)
}
// Remove re-authentication option on Android O, it will not work
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
suCategory.removePreference(reauth)
}
// Disable fingerprint option if not possible
if (!FingerprintHelper.canUseFingerprint()) {
fingerprint.isEnabled = false
fingerprint.isChecked = false
fingerprint.setSummary(R.string.disable_fingerprint)
}
if (Const.USER_ID == 0 && Info.isConnected.value && Shell.rootAccess()) {
if (activity.packageName == BuildConfig.APPLICATION_ID) {
generalCatagory.removePreference(restoreManager)
hideManager.setOnPreferenceClickListener { hideManager.setOnPreferenceClickListener {
PatchAPK.hideManager(requireContext()) showManagerNameDialog {
PatchAPK.hideManager(requireContext(), it)
}
true true
} }
val restoreManager = findPreference<Preference>("restore") } else {
restoreManager?.setOnPreferenceClickListener { generalCatagory.removePreference(hideManager)
restoreManager.setOnPreferenceClickListener {
DownloadService(requireContext()) { DownloadService(requireContext()) {
subject = DownloadSubject.Manager(Configuration.APK.Restore) subject = DownloadSubject.Manager(Configuration.APK.Restore)
} }
true true
} }
}
} else {
// Remove if not primary user, no connection, or no root
generalCatagory.removePreference(restoreManager)
generalCatagory.removePreference(hideManager)
}
if (!Utils.showSuperUser()) {
preferenceScreen.removePreference(suCategory)
}
if (!Shell.rootAccess()) {
preferenceScreen.removePreference(magiskCategory)
generalCatagory.removePreference(hideManager)
}
findPreference<Preference>("clear")?.setOnPreferenceClickListener { findPreference<Preference>("clear")?.setOnPreferenceClickListener {
Completable.fromAction { repoDB.clear() }.subscribeK { Completable.fromAction { repoDB.clear() }.subscribeK {
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT)
@ -125,58 +174,7 @@ class SettingsFragment : BasePreferenceFragment() {
setLocalePreference(findPreference(Config.Key.LOCALE)!!) setLocalePreference(findPreference(Config.Key.LOCALE)!!)
/* We only show canary channels if user is already on canary channel
* or the user have already chosen canary channel */
if (!Utils.isCanary && Config.updateChannel < Config.Value.CANARY_CHANNEL) {
// Remove the last 2 entries
val entries = updateChannel.entries
updateChannel.entries = entries.copyOf(entries.size - 2)
}
setSummary() setSummary()
// Disable dangerous settings in secondary user
if (Const.USER_ID > 0) {
suCategory.removePreference(multiuserConfig)
}
// Disable re-authentication option on Android O, it will not work
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
reauth.isEnabled = false
reauth.isChecked = false
reauth.setSummary(R.string.android_o_not_support)
}
// Disable fingerprint option if not possible
if (!FingerprintHelper.canUseFingerprint()) {
fingerprint.isEnabled = false
fingerprint.isChecked = false
fingerprint.setSummary(R.string.disable_fingerprint)
}
if (Shell.rootAccess() && Const.USER_ID == 0) {
if (activity.packageName == BuildConfig.APPLICATION_ID) {
generalCatagory.removePreference(restoreManager)
} else {
if (!Networking.checkNetworkStatus(requireContext())) {
generalCatagory.removePreference(restoreManager)
}
generalCatagory.removePreference(hideManager)
}
} else {
generalCatagory.removePreference(restoreManager)
generalCatagory.removePreference(hideManager)
}
if (!Utils.showSuperUser()) {
preferenceScreen.removePreference(suCategory)
}
if (!Shell.rootAccess()) {
preferenceScreen.removePreference(magiskCategory)
generalCatagory.removePreference(hideManager)
}
} }
override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String) { override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
@ -299,8 +297,8 @@ class SettingsFragment : BasePreferenceFragment() {
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setTitle(R.string.settings_update_custom) .setTitle(R.string.settings_update_custom)
.setView(v) .setView(v)
.setPositiveButton(R.string.ok) { _, _ -> onSuccess(url.text.toString()) } .setPositiveButton(android.R.string.ok) { _, _ -> onSuccess(url.text.toString()) }
.setNegativeButton(R.string.close) { _, _ -> onCancel() } .setNegativeButton(android.R.string.cancel) { _, _ -> onCancel() }
.setOnCancelListener { onCancel() } .setOnCancelListener { onCancel() }
.show() .show()
} }
@ -324,11 +322,35 @@ class SettingsFragment : BasePreferenceFragment() {
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setTitle(R.string.settings_download_path_title) .setTitle(R.string.settings_download_path_title)
.setView(binding.root) .setView(binding.root)
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
Utils.ensureDownloadPath(data.text.value)?.let { onSuccess(data.text.value) } Utils.ensureDownloadPath(data.text.value)?.let { onSuccess(data.text.value) }
?: Utils.toast(R.string.settings_download_path_error, Toast.LENGTH_SHORT) ?: Utils.toast(R.string.settings_download_path_error, Toast.LENGTH_SHORT)
} }
.setNegativeButton(R.string.close, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
} }
private inline fun showManagerNameDialog(
crossinline onSuccess: (String) -> Unit
) {
val data = ManagerNameData()
val view = DialogCustomNameBinding
.inflate(LayoutInflater.from(requireContext()))
.also { it.data = data }
AlertDialog.Builder(requireActivity())
.setTitle(R.string.settings_app_name)
.setView(view.root)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (view.dialogNameInput.error.isNullOrBlank()) {
onSuccess(data.name.value)
}
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
inner class ManagerNameData {
val name = KObservableField(resources.getString(R.string.re_app_name))
}
} }

View File

@ -84,8 +84,8 @@ class SuperuserViewModel(
CustomAlertDialog(this) CustomAlertDialog(this)
.setTitle(R.string.su_revoke_title) .setTitle(R.string.su_revoke_title)
.setMessage(getString(R.string.su_revoke_msg, item.item.appName)) .setMessage(getString(R.string.su_revoke_msg, item.item.appName))
.setPositiveButton(R.string.yes) { _, _ -> updateState() } .setPositiveButton(android.R.string.yes) { _, _ -> updateState() }
.setNegativeButton(R.string.no_thanks, null) .setNegativeButton(android.R.string.no, null)
.setCancelable(true) .setCancelable(true)
.show() .show()
} }

View File

@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.surequest
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils
import android.view.Window import android.view.Window
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.base.BaseActivity
@ -31,18 +30,16 @@ open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestB
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val intent = intent val intent = intent
val action = intent.action
if (TextUtils.equals(action, GeneralReceiver.REQUEST)) { when (intent?.action) {
GeneralReceiver.REQUEST -> {
if (!viewModel.handleRequest(intent)) if (!viewModel.handleRequest(intent))
finish() finish()
return return
} }
GeneralReceiver.LOG -> SuLogger.handleLogs(this, intent)
if (TextUtils.equals(action, GeneralReceiver.LOG)) GeneralReceiver.NOTIFY -> SuLogger.handleNotify(this, intent)
SuLogger.handleLogs(intent) }
else if (TextUtils.equals(action, GeneralReceiver.NOTIFY))
SuLogger.handleNotify(intent)
finish() finish()
} }

View File

@ -0,0 +1,22 @@
package com.topjohnwu.magisk.utils
class CachedValue<T>(private val factory: () -> T) : Lazy<T> {
private var _val : T? = null
override val value: T
get() {
val local = _val
return local ?: synchronized(this) {
_val ?: factory().also { _val = it }
}
}
override fun isInitialized() = _val != null
fun invalidate() {
synchronized(this) {
_val = null
}
}
}

View File

@ -31,6 +31,7 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.textfield.TextInputLayout
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.drawableCompat import com.topjohnwu.magisk.extensions.drawableCompat
import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial
@ -240,6 +241,13 @@ fun setEnabled(view: View, isEnabled: Boolean) {
view.isEnabled = isEnabled view.isEnabled = isEnabled
} }
@BindingAdapter("error")
fun TextInputLayout.setErrorString(error: String) {
val newError = error.let { if (it.isEmpty()) null else it }
if (this.error == null && newError == null) return
this.error = newError
}
// md2 // md2
@BindingAdapter("onSelectClick", "onSelectReset", requireAll = false) @BindingAdapter("onSelectClick", "onSelectReset", requireAll = false)

View File

@ -9,15 +9,11 @@ import java.io.Serializable
* You can define if wrapped type is Nullable or not. * You can define if wrapped type is Nullable or not.
* You can use kotlin get/set syntax for value * You can use kotlin get/set syntax for value
*/ */
class KObservableField<T> : ObservableField<T>, Serializable { open class KObservableField<T> : ObservableField<T>, Serializable {
var value: T var value: T
set(value) { get() = get()
if (field != value) { set(value) { set(value) }
field = value
notifyChange()
}
}
constructor(init: T) { constructor(init: T) {
value = init value = init
@ -27,23 +23,8 @@ class KObservableField<T> : ObservableField<T>, Serializable {
value = init value = init
} }
@Deprecated( @Suppress("UNCHECKED_CAST")
message = "Needed for data binding, use KObservableField.value syntax from code",
replaceWith = ReplaceWith("value")
)
override fun get(): T { override fun get(): T {
return value return super.get() as T
}
@Deprecated(
message = "Needed for data binding, use KObservableField.value = ... syntax from code",
replaceWith = ReplaceWith("value = newValue")
)
override fun set(newValue: T) {
value = newValue
}
override fun toString(): String {
return "KObservableField(value=$value)"
} }
} }

View File

@ -0,0 +1,132 @@
package com.topjohnwu.magisk.utils
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Base64OutputStream
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.signing.CryptoUtils.readCertificate
import com.topjohnwu.signing.CryptoUtils.readPrivateKey
import com.topjohnwu.superuser.internal.InternalUtils
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import org.koin.core.context.GlobalContext
import org.koin.core.context.startKoin
import timber.log.Timber
import java.io.ByteArrayOutputStream
import java.math.BigInteger
import java.security.KeyPairGenerator
import java.security.KeyStore
import java.security.MessageDigest
import java.security.PrivateKey
import java.security.cert.X509Certificate
import java.util.*
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
private interface CertKeyProvider {
val cert: X509Certificate
val key: PrivateKey
}
@Suppress("DEPRECATION")
class Keygen: CertKeyProvider {
companion object {
private const val ALIAS = "magisk"
private val PASSWORD get() = "magisk".toCharArray()
private const val TESTKEY_CERT = "61ed377e85d386a8dfee6b864bd85b0bfaa5af81"
private const val DNAME = "C=US,ST=California,L=Mountain View,O=Google Inc.,OU=Android,CN=Android"
private const val BASE64_FLAG = Base64.NO_PADDING or Base64.NO_WRAP
}
private val start = Calendar.getInstance()
private val end = Calendar.getInstance().apply { add(Calendar.YEAR, 30) }
override val cert get() = provider.cert
override val key get() = provider.key
private val provider: CertKeyProvider
inner class KeyStoreProvider : CertKeyProvider {
private val ks by lazy { init() }
override val cert by lazy { ks.getCertificate(ALIAS) as X509Certificate }
override val key by lazy { ks.getKey(ALIAS, PASSWORD) as PrivateKey }
}
class TestProvider : CertKeyProvider {
override val cert by lazy {
readCertificate(javaClass.getResourceAsStream("/keys/testkey.x509.pem"))
}
override val key by lazy {
readPrivateKey(javaClass.getResourceAsStream("/keys/testkey.pk8"))
}
}
init {
// This object could possibly be accessed from an external app
// Get context from reflection into Android's framework
val context = InternalUtils.getContext()
val pm = context.packageManager
val info = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)
val sig = info.signatures[0]
val digest = MessageDigest.getInstance("SHA1")
val chksum = digest.digest(sig.toByteArray())
val sb = StringBuilder()
for (b in chksum) {
sb.append("%02x".format(0xFF and b.toInt()))
}
provider = if (sb.toString() == TESTKEY_CERT) {
// The app was signed by the test key, continue to use it (legacy mode)
TestProvider()
} else {
KeyStoreProvider()
}
}
private fun init(): KeyStore {
GlobalContext.getOrNull() ?: {
// Invoked externally, do some basic initialization
startKoin {
modules(koinModules)
}
Timber.plant(Timber.DebugTree())
}()
val raw = Config.keyStoreRaw
val ks = KeyStore.getInstance("PKCS12")
if (raw.isEmpty()) {
ks.load(null)
} else {
GZIPInputStream(Base64.decode(raw, BASE64_FLAG).inputStream()).use {
ks.load(it, PASSWORD)
}
}
// Keys already exist
if (ks.containsAlias(ALIAS))
return ks
// Generate new private key and certificate
val kp = KeyPairGenerator.getInstance("RSA").apply { initialize(4096) }.genKeyPair()
val dname = X500Name(DNAME)
val builder = JcaX509v3CertificateBuilder(dname, BigInteger(160, Random()),
start.time, end.time, dname, kp.public)
val signer = JcaContentSignerBuilder("SHA256WithRSA").build(kp.private)
val cert = JcaX509CertificateConverter().getCertificate(builder.build(signer))
// Store them into keystore
ks.setKeyEntry(ALIAS, kp.private, PASSWORD, arrayOf(cert))
val bytes = ByteArrayOutputStream()
GZIPOutputStream(Base64OutputStream(bytes, BASE64_FLAG)).use {
ks.store(it, PASSWORD)
}
Config.keyStoreRaw = bytes.toString("UTF-8")
return ks
}
}

View File

@ -0,0 +1,49 @@
package com.topjohnwu.magisk.utils
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.content.res.Resources
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.ResourceMgr
import com.topjohnwu.magisk.extensions.langTagToLocale
import io.reactivex.Single
import java.util.*
import kotlin.Comparator
var currentLocale: Locale = Locale.getDefault()
@SuppressLint("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault()
@Suppress("DEPRECATION")
val availableLocales = Single.fromCallable {
val compareId = R.string.app_changelog
mutableListOf<Locale>().apply {
// Add default locale
add(Locale.ENGLISH)
// Add some special locales
add(Locale.TAIWAN)
add(Locale("pt", "BR"))
val config = Configuration()
val metrics = ResourceMgr.resource.displayMetrics
val res = Resources(ResourceMgr.resource.assets, metrics, config)
// Other locales
val otherLocales = ResourceMgr.resource.assets.locales
.map { it.langTagToLocale() }
.distinctBy {
config.setLocale(it)
res.updateConfiguration(config, metrics)
res.getString(compareId)
}
listOf("", "").toTypedArray()
addAll(otherLocales)
}.sortedWith(Comparator { a, b ->
a.getDisplayName(a).toLowerCase(a)
.compareTo(b.getDisplayName(b).toLowerCase(b))
})
}.cache()!!

View File

@ -1,11 +1,14 @@
package com.topjohnwu.magisk.utils package com.topjohnwu.magisk.utils
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.os.Build.VERSION.SDK_INT
import android.widget.Toast import android.widget.Toast
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.data.network.GithubRawServices
import com.topjohnwu.magisk.extensions.DynamicClassLoader
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.signing.JarMap import com.topjohnwu.signing.JarMap
import com.topjohnwu.signing.SignAPK import com.topjohnwu.signing.SignAPK
@ -47,81 +50,86 @@ object PatchAPK {
} }
private fun findAndPatch(xml: ByteArray, from: String, to: String): Boolean { private fun findAndPatch(xml: ByteArray, from: String, to: String): Boolean {
if (from.length != to.length) if (to.length > from.length)
return false return false
val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer() val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer()
val offList = mutableListOf<Int>() val offList = mutableListOf<Int>()
var i = 0 var i = 0
while (i < buf.length - from.length) { loop@ while (i < buf.length - from.length) {
var match = true for (j in from.indices) {
for (j in 0 until from.length) {
if (buf.get(i + j) != from[j]) { if (buf.get(i + j) != from[j]) {
match = false ++i
break continue@loop
} }
} }
if (match) {
offList.add(i) offList.add(i)
i += from.length i += from.length
} }
++i
}
if (offList.isEmpty()) if (offList.isEmpty())
return false return false
val toBuf = to.toCharArray().copyOf(from.length)
for (off in offList) { for (off in offList) {
buf.position(off) buf.position(off)
buf.put(to) buf.put(toBuf)
} }
return true return true
} }
private fun findAndPatch(xml: ByteArray, a: Int, b: Int): Boolean { private fun patchAndHide(context: Context, label: String): Boolean {
val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer() // If not running as stub, and we are compatible with stub, use stub
val len = xml.size / 4 val src = if (!isRunningAsStub && SDK_INT >= 28 && Info.env.connectionMode == 3) {
for (i in 0 until len) { val stub = File(context.cacheDir, "stub.apk")
if (buf.get(i) == a) { val svc = get<GithubRawServices>()
buf.put(i, b) runCatching {
return true svc.fetchFile(Info.remote.stub.link).blockingGet().byteStream().use {
} it.writeTo(stub)
} }
}.onFailure {
Timber.e(it)
return false return false
} }
stub.path
} else {
context.packageCodePath
}
private fun patchAndHide(context: Context): Boolean { // Generate a new random package name and signature
// Generate a new app with random package name val repack = File(context.cacheDir, "patched.apk")
val repack = File(context.filesDir, "patched.apk")
val pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length) val pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length)
Config.keyStoreRaw = ""
if (!patch(context.packageCodePath, repack.path, pkg)) if (!patch(src, repack.path, pkg, label))
return false return false
// Install the application // Install the application
repack.setReadable(true, false) repack.setReadable(true, false)
if (!Shell.su("pm install $repack").exec().isSuccess) if (!Shell.su("force_pm_install $repack").exec().isSuccess)
return false return false
Config.suManager = pkg Config.suManager = pkg
Config.export() Config.export()
Utils.rmAndLaunch(BuildConfig.APPLICATION_ID, Shell.su("pm uninstall ${BuildConfig.APPLICATION_ID}").submit()
ComponentName(pkg, ClassMap.get<Class<*>>(SplashActivity::class.java).name))
return true return true
} }
@JvmStatic @JvmStatic
fun patch(apk: String, out: String, pkg: String): Boolean { @JvmOverloads
fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean {
try { try {
val jar = JarMap(apk) val jar = JarMap(apk)
val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val je = jar.getJarEntry(Const.ANDROID_MANIFEST)
val xml = jar.getRawData(je) val xml = jar.getRawData(je)
if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) || if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) ||
!findAndPatch(xml, R.string.app_name, R.string.re_app_name)) !findAndPatch(xml, "Magisk Manager", label))
return false return false
// Write apk changes // Write apk changes
jar.getOutputStream(je).write(xml) jar.getOutputStream(je).write(xml)
SignAPK.sign(jar, FileOutputStream(out).buffered()) val keys = Keygen()
SignAPK.sign(keys.cert, keys.key, jar, FileOutputStream(out).buffered())
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
return false return false
@ -130,11 +138,36 @@ object PatchAPK {
return true return true
} }
fun hideManager(context: Context) { fun patch(apk: File, out: File, pkg: String, label: String): Boolean {
try {
if (apk.length() < 1 shl 18) {
// APK is smaller than 256K, must be stub
return patch(apk.path, out.path, pkg, label)
}
// Try using the new APK to patch itself
val loader = DynamicClassLoader(apk)
val cls = loader.loadClass("a.a")
for (m in cls.declaredMethods) {
val pars = m.parameterTypes
if (pars.size == 4 && pars[0] == String::class.java) {
return m.invoke(null, apk.path, out.path, pkg, label) as Boolean
}
}
throw Exception("No matching method found")
} catch (e: Exception) {
Timber.e(e)
// Fallback to use the current implementation
return patch(apk.path, out.path, pkg, label)
}
}
fun hideManager(context: Context, label: String) {
Completable.fromAction { Completable.fromAction {
val progress = Notifications.progress(context, context.getString(R.string.hide_manager_title)) val progress = Notifications.progress(context, context.getString(R.string.hide_manager_title))
Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build()) Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build())
if (!patchAndHide(context)) if (!patchAndHide(context, label))
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG)
Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID) Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID)
}.subscribeK() }.subscribeK()

View File

@ -1,126 +0,0 @@
@file:Suppress("DEPRECATION")
package com.topjohnwu.magisk.utils
import android.annotation.SuppressLint
import android.content.Context
import android.content.ContextWrapper
import android.content.res.AssetManager
import android.content.res.Configuration
import android.content.res.Resources
import androidx.annotation.StringRes
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.langTagToLocale
import io.reactivex.Single
import java.util.*
var isRunningAsStub = false
var currentLocale: Locale = Locale.getDefault()
private set
@SuppressLint("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault()
val availableLocales = Single.fromCallable {
val compareId = R.string.app_changelog
mutableListOf<Locale>().apply {
// Add default locale
add(Locale.ENGLISH)
// Add some special locales
add(Locale.TAIWAN)
add(Locale("pt", "BR"))
val config = Configuration()
val metrics = ResourceMgr.resource.displayMetrics
val res = Resources(ResourceMgr.resource.assets, metrics, config)
// Other locales
val otherLocales = ResourceMgr.resource.assets.locales
.map { it.langTagToLocale() }
.distinctBy {
config.setLocale(it)
res.updateConfiguration(config, metrics)
res.getString(compareId)
}
listOf("", "").toTypedArray()
addAll(otherLocales)
}.sortedWith(Comparator { a, b ->
a.getDisplayName(a).toLowerCase(a)
.compareTo(b.getDisplayName(b).toLowerCase(b))
})
}.cache()!!
private val addAssetPath by lazy {
AssetManager::class.java.getMethod("addAssetPath", String::class.java)
}
fun AssetManager.addAssetPath(path: String) {
addAssetPath.invoke(this, path)
}
fun Context.wrap(global: Boolean = true): Context
= if (!global) ResourceMgr.ResContext(this) else ResourceMgr.GlobalResContext(this)
object ResourceMgr {
lateinit var resource: Resources
private lateinit var resApk: String
fun init(context: Context) {
resource = context.resources
if (isRunningAsStub)
resApk = DynAPK.current(context).path
}
// Override locale and inject resources from dynamic APK
private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources {
config.setLocale(currentLocale)
updateConfiguration(config, displayMetrics)
if (isRunningAsStub)
assets.addAssetPath(resApk)
return this
}
fun reload(config: Configuration = Configuration(resource.configuration)) {
val localeConfig = Config.locale
currentLocale = when {
localeConfig.isEmpty() -> defaultLocale
else -> localeConfig.langTagToLocale()
}
Locale.setDefault(currentLocale)
resource.patch(config)
}
fun getString(locale: Locale, @StringRes id: Int): String {
val config = Configuration()
config.setLocale(locale)
return Resources(resource.assets, resource.displayMetrics, config).getString(id)
}
open class GlobalResContext(base: Context) : ContextWrapper(base) {
open val mRes: Resources get() = resource
private val loader by lazy { javaClass.classLoader!! }
override fun getResources(): Resources {
return mRes
}
override fun getClassLoader(): ClassLoader {
return loader
}
override fun createConfigurationContext(config: Configuration): Context {
return ResContext(super.createConfigurationContext(config))
}
}
class ResContext(base: Context) : GlobalResContext(base) {
override val mRes by lazy { base.resources.patch() }
}
}

View File

@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.rawResource import com.topjohnwu.magisk.extensions.rawResource
import com.topjohnwu.magisk.wrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
@ -16,12 +17,14 @@ class RootInit : Shell.Initializer() {
} }
fun init(context: Context, shell: Shell): Boolean { fun init(context: Context, shell: Shell): Boolean {
// Invalidate env state if shell is recreated
Info.envRef.invalidate()
val job = shell.newJob() val job = shell.newJob()
if (shell.isRoot) { if (shell.isRoot) {
job.add(context.rawResource(R.raw.util_functions)) job.add(context.rawResource(R.raw.util_functions))
.add(context.rawResource(R.raw.utils)) .add(context.rawResource(R.raw.utils))
Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk") Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk")
Info.loadMagiskInfo()
} else { } else {
job.add(context.rawResource(R.raw.nonroot_utils)) job.add(context.rawResource(R.raw.nonroot_utils))
} }
@ -35,6 +38,7 @@ class RootInit : Shell.Initializer() {
Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
return true return true
} }
} }

View File

@ -2,14 +2,13 @@ package com.topjohnwu.magisk.utils
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.os.Process import android.os.Process
import android.widget.Toast import android.widget.Toast
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.data.repository.LogRepository import com.topjohnwu.magisk.data.repository.LogRepository
import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.toLog import com.topjohnwu.magisk.model.entity.toLog
import com.topjohnwu.magisk.model.entity.toPolicy import com.topjohnwu.magisk.model.entity.toPolicy
@ -17,15 +16,13 @@ import java.util.*
object SuLogger { object SuLogger {
private val context: Context by inject() fun handleLogs(context: Context, intent: Intent) {
fun handleLogs(intent: Intent) {
val fromUid = intent.getIntExtra("from.uid", -1) val fromUid = intent.getIntExtra("from.uid", -1)
if (fromUid < 0) return if (fromUid < 0) return
if (fromUid == Process.myUid()) return if (fromUid == Process.myUid()) return
val pm: PackageManager by inject() val pm = context.packageManager
val notify: Boolean val notify: Boolean
val data = intent.extras val data = intent.extras
@ -36,7 +33,7 @@ object SuLogger {
}.getOrElse { return } }.getOrElse { return }
} else { } else {
// Doesn't report whether notify or not, check database ourselves // Doesn't report whether notify or not, check database ourselves
val policyDB: PolicyDao by inject() val policyDB = get<PolicyDao>()
val policy = policyDB.fetch(fromUid).blockingGet() ?: return val policy = policyDB.fetch(fromUid).blockingGet() ?: return
notify = policy.notification notify = policy.notification
policy policy
@ -46,7 +43,7 @@ object SuLogger {
return return
if (notify) if (notify)
handleNotify(policy) handleNotify(context, policy)
val toUid = intent.getIntExtra("to.uid", -1) val toUid = intent.getIntExtra("to.uid", -1)
if (toUid < 0) return if (toUid < 0) return
@ -62,11 +59,11 @@ object SuLogger {
date = Date() date = Date()
) )
val logRepo: LogRepository by inject() val logRepo = get<LogRepository>()
logRepo.put(log).blockingGet()?.printStackTrace() logRepo.put(log).blockingGet()?.printStackTrace()
} }
private fun handleNotify(policy: MagiskPolicy) { private fun handleNotify(context: Context, policy: MagiskPolicy) {
if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) { if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) {
Utils.toast( Utils.toast(
context.getString( context.getString(
@ -80,16 +77,16 @@ object SuLogger {
} }
} }
fun handleNotify(intent: Intent) { fun handleNotify(context: Context, intent: Intent) {
val fromUid = intent.getIntExtra("from.uid", -1) val fromUid = intent.getIntExtra("from.uid", -1)
if (fromUid < 0) return if (fromUid < 0) return
if (fromUid == Process.myUid()) return if (fromUid == Process.myUid()) return
runCatching { runCatching {
val packageManager: PackageManager by inject() val pm = context.packageManager
val policy = fromUid.toPolicy(packageManager) val policy = fromUid.toPolicy(pm)
.copy(policy = intent.getIntExtra("policy", -1)) .copy(policy = intent.getIntExtra("policy", -1))
if (policy.policy >= 0) if (policy.policy >= 0)
handleNotify(policy) handleNotify(context, policy)
} }
} }
} }

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk.utils package com.topjohnwu.magisk.utils
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
@ -46,7 +45,7 @@ object Utils {
.setRequiresDeviceIdle(true) .setRequiresDeviceIdle(true)
.build() .build()
val request = PeriodicWorkRequest val request = PeriodicWorkRequest
.Builder(ClassMap[UpdateCheckService::class.java], 12, TimeUnit.HOURS) .Builder(ClassMap[UpdateCheckService::class.java] as Class<Worker>, 12, TimeUnit.HOURS)
.setConstraints(constraints) .setConstraints(constraints)
.build() .build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork( WorkManager.getInstance(context).enqueueUniquePeriodicWork(
@ -73,8 +72,4 @@ object Utils {
if ((exists() && isDirectory) || mkdirs()) this else null if ((exists() && isDirectory) || mkdirs()) this else null
} }
fun rmAndLaunch(rm: String, component: ComponentName) {
Shell.su("(rm_launch $rm ${component.flattenToString()})").exec()
}
} }

View File

@ -48,7 +48,7 @@ object MarkDownWindow : KoinComponent {
AlertDialog.Builder(activity) AlertDialog.Builder(activity)
.setTitle(title) .setTitle(title)
.setView(mv) .setView(mv)
.setNegativeButton(R.string.close) { dialog, _ -> dialog.dismiss() } .setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.dismiss() }
.show() .show()
} }
} }

View File

@ -4,15 +4,11 @@ import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Build import android.os.Build
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.app.TaskStackBuilder import androidx.core.app.TaskStackBuilder
import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.ui.SplashActivity
@ -20,6 +16,7 @@ import com.topjohnwu.magisk.ui.SplashActivity
object Notifications { object Notifications {
val mgr by lazy { NotificationManagerCompat.from(get()) } val mgr by lazy { NotificationManagerCompat.from(get()) }
private val icon by lazy { resolveRes(DynAPK.NOTIFICATION) }
fun setup(context: Context) { fun setup(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -34,16 +31,16 @@ object Notifications {
} }
fun magiskUpdate(context: Context) { fun magiskUpdate(context: Context) {
val intent = Intent(context, ClassMap[SplashActivity::class.java]) val intent = context.intent(SplashActivity::class.java)
intent.putExtra(Const.Key.OPEN_SECTION, "magisk") .putExtra(Const.Key.OPEN_SECTION, "magisk")
val stackBuilder = TaskStackBuilder.create(context) val stackBuilder = TaskStackBuilder.create(context)
stackBuilder.addParentStack(ClassMap.get<Class<*>>(SplashActivity::class.java)) stackBuilder.addParentStack(SplashActivity::class.java.cmp(context.packageName))
stackBuilder.addNextIntent(intent) stackBuilder.addNextIntent(intent)
val pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, val pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL)
builder.setSmallIcon(R.drawable.ic_magisk_outline) builder.setSmallIcon(icon)
.setContentTitle(context.getString(R.string.magisk_update_title)) .setContentTitle(context.getString(R.string.magisk_update_title))
.setContentText(context.getString(R.string.manager_download_install)) .setContentText(context.getString(R.string.manager_download_install))
.setVibrate(longArrayOf(0, 100, 100, 100)) .setVibrate(longArrayOf(0, 100, 100, 100))
@ -54,15 +51,15 @@ object Notifications {
} }
fun managerUpdate(context: Context) { fun managerUpdate(context: Context) {
val intent = Intent(context, ClassMap[GeneralReceiver::class.java]) val intent = context.intent(GeneralReceiver::class.java)
intent.action = Const.Key.BROADCAST_MANAGER_UPDATE .setAction(Const.Key.BROADCAST_MANAGER_UPDATE)
intent.putExtra(Const.Key.INTENT_SET_APP, Info.remote.app) .putExtra(Const.Key.INTENT_SET_APP, Info.remote.app)
val pendingIntent = PendingIntent.getBroadcast(context, val pendingIntent = PendingIntent.getBroadcast(context,
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL)
builder.setSmallIcon(R.drawable.ic_magisk_outline) builder.setSmallIcon(icon)
.setContentTitle(context.getString(R.string.manager_update_title)) .setContentTitle(context.getString(R.string.manager_update_title))
.setContentText(context.getString(R.string.manager_download_install)) .setContentText(context.getString(R.string.manager_download_install))
.setVibrate(longArrayOf(0, 100, 100, 100)) .setVibrate(longArrayOf(0, 100, 100, 100))
@ -73,13 +70,13 @@ object Notifications {
} }
fun dtboPatched(context: Context) { fun dtboPatched(context: Context) {
val intent = Intent(context, ClassMap[GeneralReceiver::class.java]) val intent = context.intent(GeneralReceiver::class.java)
.setAction(Const.Key.BROADCAST_REBOOT) .setAction(Const.Key.BROADCAST_REBOOT)
val pendingIntent = PendingIntent.getBroadcast(context, val pendingIntent = PendingIntent.getBroadcast(context,
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL)
builder.setSmallIcon(R.drawable.ic_magisk_outline) builder.setSmallIcon(icon)
.setContentTitle(context.getString(R.string.dtbo_patched_title)) .setContentTitle(context.getString(R.string.dtbo_patched_title))
.setContentText(context.getString(R.string.dtbo_patched_reboot)) .setContentText(context.getString(R.string.dtbo_patched_reboot))
.setVibrate(longArrayOf(0, 100, 100, 100)) .setVibrate(longArrayOf(0, 100, 100, 100))

View File

@ -15,55 +15,56 @@ import com.topjohnwu.superuser.Shell
object Shortcuts { object Shortcuts {
fun setup(context: Context) { fun setup(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { if (Build.VERSION.SDK_INT >= 25) {
val manager = context.getSystemService(ShortcutManager::class.java) val manager = context.getSystemService(ShortcutManager::class.java)
manager?.dynamicShortcuts = getShortCuts(context) manager?.dynamicShortcuts = getShortCuts(context)
} }
} }
@RequiresApi(api = Build.VERSION_CODES.N_MR1) @RequiresApi(api = 25)
private fun getShortCuts(context: Context): List<ShortcutInfo> { private fun getShortCuts(context: Context): List<ShortcutInfo> {
val shortCuts = mutableListOf<ShortcutInfo>() val shortCuts = mutableListOf<ShortcutInfo>()
val root = Shell.rootAccess() val root = Shell.rootAccess()
val intent = context.intent(SplashActivity::class.java)
if (Utils.showSuperUser()) { if (Utils.showSuperUser()) {
shortCuts.add(ShortcutInfo.Builder(context, "superuser") shortCuts.add(ShortcutInfo.Builder(context, "superuser")
.setShortLabel(context.getString(R.string.superuser)) .setShortLabel(context.getString(R.string.superuser))
.setIntent(Intent(context, ClassMap[SplashActivity::class.java]) .setIntent(Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "superuser") .putExtra(Const.Key.OPEN_SECTION, "superuser")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(context, R.drawable.sc_superuser)) .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.SUPERUSER)))
.setRank(0) .setRank(0)
.build()) .build())
} }
if (root && Config.magiskHide) { if (root && Info.env.magiskHide) {
shortCuts.add(ShortcutInfo.Builder(context, "magiskhide") shortCuts.add(ShortcutInfo.Builder(context, "magiskhide")
.setShortLabel(context.getString(R.string.magiskhide)) .setShortLabel(context.getString(R.string.magiskhide))
.setIntent(Intent(context, ClassMap[SplashActivity::class.java]) .setIntent(Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "magiskhide") .putExtra(Const.Key.OPEN_SECTION, "magiskhide")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(context, R.drawable.sc_magiskhide)) .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.MAGISKHIDE)))
.setRank(1) .setRank(1)
.build()) .build())
} }
if (!Config.coreOnly && root && Info.magiskVersionCode >= 0) { if (!Config.coreOnly && root && Info.env.magiskVersionCode >= 0) {
shortCuts.add(ShortcutInfo.Builder(context, "modules") shortCuts.add(ShortcutInfo.Builder(context, "modules")
.setShortLabel(context.getString(R.string.modules)) .setShortLabel(context.getString(R.string.modules))
.setIntent(Intent(context, ClassMap[SplashActivity::class.java]) .setIntent(Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "modules") .putExtra(Const.Key.OPEN_SECTION, "modules")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(context, R.drawable.sc_extension)) .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.MODULES)))
.setRank(3) .setRank(3)
.build()) .build())
shortCuts.add(ShortcutInfo.Builder(context, "downloads") shortCuts.add(ShortcutInfo.Builder(context, "downloads")
.setShortLabel(context.getString(R.string.downloads)) .setShortLabel(context.getString(R.string.downloads))
.setIntent(Intent(context, ClassMap[SplashActivity::class.java]) .setIntent(Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "downloads") .putExtra(Const.Key.OPEN_SECTION, "downloads")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(context, R.drawable.sc_cloud_download)) .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.DOWNLOAD)))
.setRank(2) .setRank(2)
.build()) .build())
} }

View File

@ -23,7 +23,7 @@ class EnvFixDialog(activity: Activity) : CustomAlertDialog(activity) {
setTitle(R.string.env_fix_title) setTitle(R.string.env_fix_title)
setMessage(R.string.env_fix_msg) setMessage(R.string.env_fix_msg)
setCancelable(true) setCancelable(true)
setPositiveButton(R.string.yes) { _, _ -> setPositiveButton(android.R.string.yes) { _, _ ->
val pd = ProgressDialog.show(activity, val pd = ProgressDialog.show(activity,
activity.getString(R.string.setup_title), activity.getString(R.string.setup_title),
activity.getString(R.string.setup_msg)) activity.getString(R.string.setup_msg))
@ -46,6 +46,6 @@ class EnvFixDialog(activity: Activity) : CustomAlertDialog(activity) {
} }
}.exec() }.exec()
} }
setNegativeButton(R.string.no_thanks, null) setNegativeButton(android.R.string.no, null)
} }
} }

View File

@ -31,7 +31,7 @@ class FingerprintAuthDialog(activity: Activity, private val callback: () -> Unit
binding.message.compoundDrawablePadding = Utils.dpInPx(20) binding.message.compoundDrawablePadding = Utils.dpInPx(20)
binding.message.gravity = Gravity.CENTER binding.message.gravity = Gravity.CENTER
setMessage(R.string.auth_fingerprint) setMessage(R.string.auth_fingerprint)
setNegativeButton(R.string.close) { _, _ -> setNegativeButton(android.R.string.cancel) { _, _ ->
helper?.cancel() helper?.cancel()
failureCallback?.invoke() failureCallback?.invoke()
} }

View File

@ -62,12 +62,12 @@ internal class InstallMethodDialog(activity: BaseActivity<*, *>, options: List<S
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setMessage(R.string.install_inactive_slot_msg) .setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true) .setCancelable(true)
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(android.R.string.yes) { _, _ ->
DownloadService(activity) { DownloadService(activity) {
subject = DownloadSubject.Magisk(Configuration.Flash.Secondary) subject = DownloadSubject.Magisk(Configuration.Flash.Secondary)
} }
} }
.setNegativeButton(R.string.no_thanks, null) .setNegativeButton(android.R.string.no, null)
.show() .show()
} }
} }

View File

@ -32,7 +32,7 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<FrameLayout <androidx.fragment.app.FragmentContainerView
android:id="@+id/main_nav_host" android:id="@+id/main_nav_host"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -0,0 +1,46 @@
<?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.extensions.XStringKt" />
<variable
name="data"
type="com.topjohnwu.magisk.ui.settings.SettingsFragment.ManagerNameData" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingStart="@dimen/margin_generic"
android:paddingTop="@dimen/margin_generic"
android:paddingEnd="@dimen/margin_generic">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/dialog_name_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/settings_app_name_hint"
app:counterEnabled="true"
app:counterMaxLength="14"
app:counterOverflowTextColor="@color/colorError"
app:error="@{data.name.length() > 14 || data.name.empty || XStringKt.isEmptyInternal(data.name) ? @string/settings_app_name_error : @string/empty}"
app:errorEnabled="true"
app:helperText="@string/settings_app_name_helper">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={data.name}"
tools:text="@string/re_app_name" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</layout>

View File

@ -110,8 +110,12 @@ EOF
cd / cd /
} }
rm_launch() { force_pm_install() {
pm uninstall $1 local APK=$1
am start -n $2 local VERIFY=`settings get global package_verifier_enable`
exit [ "$VERIFY" -eq 1 ] && settings put global package_verifier_enable 0
pm install -r $APK
local res=$?
[ "$VERIFY" -eq 1 ] && settings put global package_verifier_enable 1
return $res
} }

View File

@ -89,7 +89,6 @@
<string name="reboot_delay_toast">إعادة التشغيل خلال خمس ثواني…</string> <string name="reboot_delay_toast">إعادة التشغيل خلال خمس ثواني…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">إغلاق</string>
<string name="repo_install_title">تثبيت %1$s</string> <string name="repo_install_title">تثبيت %1$s</string>
<string name="repo_install_msg">هل تريد تثبيت %1$s ?</string> <string name="repo_install_msg">هل تريد تثبيت %1$s ?</string>
<string name="download">التنزيل</string> <string name="download">التنزيل</string>
@ -184,7 +183,6 @@
<string name="global_summary">تستخدم كافة جلسات العمل للروت مساحة الاسم ذات التركيب العامة</string> <string name="global_summary">تستخدم كافة جلسات العمل للروت مساحة الاسم ذات التركيب العامة</string>
<string name="requester_summary">سترث جلسات العمل للروت مساحة الأسماء لطالبيها</string> <string name="requester_summary">سترث جلسات العمل للروت مساحة الأسماء لطالبيها</string>
<string name="isolate_summary">سيكون لكل جلسة عمل للروت مساحة اسم معزولة خاصة بها </string> <string name="isolate_summary">سيكون لكل جلسة عمل للروت مساحة اسم معزولة خاصة بها </string>
<string name="android_o_not_support">لا يدعم إصدار الأندرويد +8.0</string>
<string name="disable_fingerprint">لم تُعين بصمات الأصابع أو لا يوجد قارئ بصمات</string> <string name="disable_fingerprint">لم تُعين بصمات الأصابع أو لا يوجد قارئ بصمات</string>
<string name="settings_download_path_error">خطأ عند إنشاء مجلد. عليه أن يكون سهلا الوصول إليه من خلال مجلد التخزين للروت و ألا يكون ملفا.</string> <string name="settings_download_path_error">خطأ عند إنشاء مجلد. عليه أن يكون سهلا الوصول إليه من خلال مجلد التخزين للروت و ألا يكون ملفا.</string>

View File

@ -1,122 +1,70 @@
<resources> <resources>
<!--Welcome Activity--> <!--Welcome Activity-->
<string name="modules">Əlavələr</string> <string name="modules">Əlavələr</string>
<string name="downloads">Endirmələr</string> <string name="downloads">Endirmələr</string>
<string name="superuser">Superuser</string> <string name="superuser">Superuser</string>
<string name="log">Log</string> <string name="log">Log</string>
<string name="settings">Tənzimləmələr</string> <string name="settings">Tənzimləmələr</string>
<string name="install">Quraşdır</string> <string name="install">Quraşdır</string>
<string name="unsupport_magisk_title">Dəstəklənməyən Magisk Versiyası</string> <string name="unsupport_magisk_title">Dəstəklənməyən Magisk Versiyası</string>
<string name="unsupport_magisk_message">Magisk Manager\'in bu versiyası Magisk\'in v18.0 versiyasndan aşağısını dəstəkləmir.\n\nMagisk\'i əllə yüksəldə, yaxud tətbiqi əvvəlki versiyalarına qaytara bilərsiniz.</string> <string name="unsupport_magisk_message">Magisk Manager\'in bu versiyası Magisk\'in v18.0 versiyasndan aşağısını dəstəkləmir.\n\nMagisk\'i əllə yüksəldə, yaxud tətbiqi əvvəlki versiyalarına qaytara bilərsiniz.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk yüklənməyib.</string> <string name="magisk_version_error">Magisk yüklənməyib.</string>
<string name="checking_for_updates">Yeniləmələr yoxlanılır…</string> <string name="checking_for_updates">Yeniləmələr yoxlanılır…</string>
<string name="invalid_update_channel">Etibarsız Yeniləmə Kanalı</string> <string name="invalid_update_channel">Etibarsız Yeniləmə Kanalı</string>
<string name="safetyNet_check_text">SafetyNet vəziyətinə bax</string> <string name="safetyNet_check_text">SafetyNet vəziyətinə bax</string>
<string name="checking_safetyNet_status">SafetyNet vəziyəti yoxlanılır…</string> <string name="checking_safetyNet_status">SafetyNet vəziyəti yoxlanılır…</string>
<string name="safetyNet_check_success">SafetyNet Uğurla Yoxlanıldı</string> <string name="safetyNet_check_success">SafetyNet Uğurla Yoxlanıldı</string>
<string name="safetyNet_api_error">SafetyNet API Xətası</string> <string name="safetyNet_api_error">SafetyNet API Xətası</string>
<string name="safetyNet_res_invalid">Cavab etibarsızdır.</string> <string name="safetyNet_res_invalid">Cavab etibarsızdır.</string>
<string name="magisk_up_to_date">Magisk ən yenidir</string> <string name="magisk_up_to_date">Magisk ən yenidir</string>
<string name="manager_up_to_date">Magisk Manager ən yenidir</string> <string name="manager_up_to_date">Magisk Manager ən yenidir</string>
<string name="advanced_settings_title">Qabaqcıl Parametrlər</string> <string name="advanced_settings_title">Qabaqcıl Parametrlər</string>
<string name="keep_force_encryption">Şifrələməyə məcbur etməni qoru</string> <string name="keep_force_encryption">Şifrələməyə məcbur etməni qoru</string>
<string name="keep_dm_verity">AVB 2.0/dm-verity\'i qoru</string> <string name="keep_dm_verity">AVB 2.0/dm-verity\'i qoru</string>
<string name="current_installed">Yüklənən: %1$s</string> <string name="current_installed">Yüklənən: %1$s</string>
<string name="latest_version">Ən son: %1$s</string> <string name="latest_version">Ən son: %1$s</string>
<string name="uninstall">Sil</string> <string name="uninstall">Sil</string>
<string name="uninstall_magisk_title">Magisk\'i Sil</string> <string name="uninstall_magisk_title">Magisk\'i Sil</string>
<string name="uninstall_magisk_msg">Bütün əlavələr ləğv olunacaq/silinəcək. Root silinəcək, və əgər hal-hazırda deyilsə, bütün məlumatlarınız potensiyal olaraq şifrələnəcək.</string> <string name="uninstall_magisk_msg">Bütün əlavələr ləğv olunacaq/silinəcək. Root silinəcək, və əgər hal-hazırda deyilsə, bütün məlumatlarınız potensiyal olaraq şifrələnəcək.</string>
<string name="update">Yenilə</string> <string name="update">Yenilə</string>
<string name="core_only_enabled">(Yalnız nüvə modu qoşulub)</string> <string name="core_only_enabled">(Yalnız nüvə modu qoşulub)</string>
<!--Module Fragment--> <!--Module Fragment-->
<string name="no_info_provided">(Məlumat təmin edilməyib)</string> <string name="no_info_provided">(Məlumat təmin edilməyib)</string>
<string name="no_modules_found">Əlavələr yoxdur.</string> <string name="no_modules_found">Əlavələr yoxdur.</string>
<string name="update_file_created">Əlavə sonrakı yenidən başlatmada yenilənəcək.</string> <string name="update_file_created">Əlavə sonrakı yenidən başlatmada yenilənəcək.</string>
<string name="remove_file_created">Əlavə sonrakı yenidən başlatmada silinəcək.</string> <string name="remove_file_created">Əlavə sonrakı yenidən başlatmada silinəcək.</string>
<string name="remove_file_deleted">Əlavə sonrakı yenidən başlatmada silinməyəcək.</string> <string name="remove_file_deleted">Əlavə sonrakı yenidən başlatmada silinməyəcək.</string>
<string name="disable_file_created">Əlavə sonrakı yenidən başlatmada qapadılacaq.</string> <string name="disable_file_created">Əlavə sonrakı yenidən başlatmada qapadılacaq.</string>
<string name="disable_file_removed">Əlavə sonrakı yenidən başlatmada açılacaq.</string> <string name="disable_file_removed">Əlavə sonrakı yenidən başlatmada açılacaq.</string>
<string name="author">%1$s tərəfindən yaradılıb</string> <string name="author">%1$s tərəfindən yaradılıb</string>
<string name="reboot_recovery">Bərpa rejimində yenidən başlat</string> <string name="reboot_recovery">Bərpa rejimində yenidən başlat</string>
<string name="reboot_bootloader">Bootloader\'ə yenidən başlat</string> <string name="reboot_bootloader">Bootloader\'ə yenidən başlat</string>
<string name="reboot_download">Yükləmə rejimində yenidən başlat</string> <string name="reboot_download">Yükləmə rejimində yenidən başlat</string>
<string name="reboot_edl">EDL\'ə yenidən başlat</string> <string name="reboot_edl">EDL\'ə yenidən başlat</string>
<!--Repo Fragment--> <!--Repo Fragment-->
<string name="update_available">Yeniləmə Var</string> <string name="update_available">Yeniləmə Var</string>
<string name="installed">Yüklənib</string> <string name="installed">Yüklənib</string>
<string name="not_installed">Yüklənməyib</string> <string name="not_installed">Yüklənməyib</string>
<string name="updated_on">Yeniləmə vaxtı: %1$s</string> <string name="updated_on">Yeniləmə vaxtı: %1$s</string>
<string name="sorting_order">Nizamlama Qaydası</string> <string name="sorting_order">Nizamlama Qaydası</string>
<string name="sort_by_name">Ada görə nizamla</string> <string name="sort_by_name">Ada görə nizamla</string>
<string name="sort_by_update">Son yeniləməyə görə nizamla</string> <string name="sort_by_update">Son yeniləməyə görə nizamla</string>
<!--Log Fragment--> <!--Log Fragment-->
<string name="menuSaveLog">Log\'u saxla</string> <string name="menuSaveLog">Log\'u saxla</string>
<string name="menuReload">Təzələ</string> <string name="menuReload">Təzələ</string>
<string name="menuClearLog">Log\'u indi təmizlə</string> <string name="menuClearLog">Log\'u indi təmizlə</string>
<string name="logs_cleared">Log uğurla təmizləndi.</string> <string name="logs_cleared">Log uğurla təmizləndi.</string>
<!--About Activity--> <!--About Activity-->
<string name="app_changelog">Yeniliklər</string> <string name="app_changelog">Yeniliklər</string>
<!-- System Components, Notifications --> <!-- System Components, Notifications -->
<string name="update_channel">Magisk Yeniləmələri</string> <string name="update_channel">Magisk Yeniləmələri</string>
<string name="progress_channel">Nəticə Bildirişləri</string> <string name="progress_channel">Nəticə Bildirişləri</string>
<string name="download_complete">Yükləmə bitdi</string> <string name="download_complete">Yükləmə bitdi</string>
@ -127,262 +75,135 @@
<string name="manager_update_title">Magisk Manager Yeniləməsi Var!</string> <string name="manager_update_title">Magisk Manager Yeniləməsi Var!</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Qapat</string>
<string name="repo_install_title">%1$s faylını yüklə</string> <string name="repo_install_title">%1$s faylını yüklə</string>
<string name="repo_install_msg">%1$s faylını indi yükləmək istəyirsiniz?</string> <string name="repo_install_msg">%1$s faylını indi yükləmək istəyirsiniz?</string>
<string name="download">Yüklə</string> <string name="download">Yüklə</string>
<string name="reboot">Yenidən Başlat</string> <string name="reboot">Yenidən Başlat</string>
<string name="settings_reboot_toast">Tənzimləmələri saxlamaq üçün yenidən başladın.</string> <string name="settings_reboot_toast">Tənzimləmələri saxlamaq üçün yenidən başladın.</string>
<string name="release_notes">Yeniliklər</string> <string name="release_notes">Yeniliklər</string>
<string name="repo_cache_cleared">Repo keşi silindi</string> <string name="repo_cache_cleared">Repo keşi silindi</string>
<string name="manager_download_install">Yükləyib quraşdırmaq üçün toxun.</string> <string name="manager_download_install">Yükləyib quraşdırmaq üçün toxun.</string>
<string name="dtbo_patched_title">DTBO yamaqlanıb!</string> <string name="dtbo_patched_title">DTBO yamaqlanıb!</string>
<string name="dtbo_patched_reboot">Magisk Manager dtbo.img\'ni yamaqladı. Xahiş olunur yenidən başladın.</string> <string name="dtbo_patched_reboot">Magisk Manager dtbo.img\'ni yamaqladı. Xahiş olunur yenidən başladın.</string>
<string name="flashing">Qurulur</string> <string name="flashing">Qurulur</string>
<string name="hide_manager_title">Magisk Manager gizlədilir…</string> <string name="hide_manager_title">Magisk Manager gizlədilir…</string>
<string name="hide_manager_fail_toast">Magisk Manager\'i gizlətmək alınmadı.</string> <string name="hide_manager_fail_toast">Magisk Manager\'i gizlətmək alınmadı.</string>
<string name="open_link_failed_toast">Keçid açmağa heçbir tətbiq tapılmadı.</string> <string name="open_link_failed_toast">Keçid açmağa heçbir tətbiq tapılmadı.</string>
<string name="download_zip_only">Yalnız Zip yüklə</string> <string name="download_zip_only">Yalnız Zip yüklə</string>
<string name="direct_install">Birdəfəlik Yüklə (Tövsiyə olunur)</string> <string name="direct_install">Birdəfəlik Yüklə (Tövsiyə olunur)</string>
<string name="select_patch_file">Fayl Seç və Yamaqla</string> <string name="select_patch_file">Fayl Seç və Yamaqla</string>
<string name="install_inactive_slot">Fəal olmayan slota quraşdır (OTA\'dan sonra)</string> <string name="install_inactive_slot">Fəal olmayan slota quraşdır (OTA\'dan sonra)</string>
<string name="warning">Xəbərdarlıq</string> <string name="warning">Xəbərdarlıq</string>
<string name="install_inactive_slot_msg">Cihazınız yenidən başladıldıqdan sonra fəal olmayan slota başlamağa MƏCBUR ediləcək!\nBu seçimi yalnız OTA bitdikdən sonra istifadə edin.\nDavam edirsiniz?</string> <string name="install_inactive_slot_msg">Cihazınız yenidən başladıldıqdan sonra fəal olmayan slota başlamağa MƏCBUR ediləcək!\nBu seçimi yalnız OTA bitdikdən sonra istifadə edin.\nDavam edirsiniz?</string>
<string name="select_method">Üsul Seçin</string> <string name="select_method">Üsul Seçin</string>
<string name="complete_uninstall">Silməni Bitir</string> <string name="complete_uninstall">Silməni Bitir</string>
<string name="restore_img">Surətləri Qaytar</string> <string name="restore_img">Surətləri Qaytar</string>
<string name="restore_img_msg">Geri qaytarılır…</string> <string name="restore_img_msg">Geri qaytarılır…</string>
<string name="restore_done">Geri qaytarma bitdi!</string> <string name="restore_done">Geri qaytarma bitdi!</string>
<string name="restore_fail">Stock nüsxə mövcud deyil!</string> <string name="restore_fail">Stock nüsxə mövcud deyil!</string>
<string name="proprietary_title">Özəl kodu yükləyin</string> <string name="proprietary_title">Özəl kodu yükləyin</string>
<string name="proprietary_notice">Magisk Manager açıq lisenziyalıdır və Google\'ın özəl SafetyNet API kodunu ehtiva etmir.\n\Magisk Managerə SafetyNet yoxlamaları üçün tərkibində GoogleApiClient olan əlavəni yükləməyə icazə verirsiniz?</string> <string name="proprietary_notice">Magisk Manager açıq lisenziyalıdır və Google\'ın özəl SafetyNet API kodunu ehtiva etmir.\n\Magisk Managerə SafetyNet yoxlamaları üçün tərkibində GoogleApiClient olan əlavəni yükləməyə icazə verirsiniz?</string>
<string name="setup_fail">Quraşdırma alınmadı.</string> <string name="setup_fail">Quraşdırma alınmadı.</string>
<string name="env_fix_title">Əlavə Quraşdırma Lazımdır</string> <string name="env_fix_title">Əlavə Quraşdırma Lazımdır</string>
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün işləməsi üçün əlavə quraşdırmaya ehtiyacı var . Bu Magisk zip faylını endirəcək, davam etmək istəyirsiniz?</string> <string name="env_fix_msg">Cihazınızın Magisk\'in düzgün işləməsi üçün əlavə quraşdırmaya ehtiyacı var . Bu Magisk zip faylını endirəcək, davam etmək istəyirsiniz?</string>
<string name="setup_title">Əlavə quraşdırma</string> <string name="setup_title">Əlavə quraşdırma</string>
<string name="setup_msg">Quraşdırma yerinə yetirilir…</string> <string name="setup_msg">Quraşdırma yerinə yetirilir…</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">Ümumi</string> <string name="settings_general_category">Ümumi</string>
<string name="settings_dark_theme_title">Qaranlıq Mövzu</string> <string name="settings_dark_theme_title">Qaranlıq Mövzu</string>
<string name="settings_dark_theme_summary">Qaranlıq mövzunu aç.</string> <string name="settings_dark_theme_summary">Qaranlıq mövzunu aç.</string>
<string name="settings_clear_cache_title">Repo Keşini Təmizlə</string> <string name="settings_clear_cache_title">Repo Keşini Təmizlə</string>
<string name="settings_clear_cache_summary">Onlayn repolar üçün keşlənmiş məlumatı silin. Bu tətbiqi onlayn şəkildə yenilənməyə məcbur edir.</string> <string name="settings_clear_cache_summary">Onlayn repolar üçün keşlənmiş məlumatı silin. Bu tətbiqi onlayn şəkildə yenilənməyə məcbur edir.</string>
<string name="settings_hide_manager_title">Magisk Manager\'i Gizlə</string> <string name="settings_hide_manager_title">Magisk Manager\'i Gizlə</string>
<string name="settings_hide_manager_summary">Magisk Manager\'i təsadüfi adla yenidən sıxışdır.</string> <string name="settings_hide_manager_summary">Magisk Manager\'i təsadüfi adla yenidən sıxışdır.</string>
<string name="settings_restore_manager_title">Magisk Manager\'i Geri Qaytar</string> <string name="settings_restore_manager_title">Magisk Manager\'i Geri Qaytar</string>
<string name="settings_restore_manager_summary">Magisk Manager\'i orjinal sıxışdırma ilə geri qaytar</string> <string name="settings_restore_manager_summary">Magisk Manager\'i orjinal sıxışdırma ilə geri qaytar</string>
<string name="language">Dil</string> <string name="language">Dil</string>
<string name="system_default">(Sistem Dili)</string> <string name="system_default">(Sistem Dili)</string>
<string name="settings_update">Tənzimləmələri Yenilə</string> <string name="settings_update">Tənzimləmələri Yenilə</string>
<string name="settings_check_update_title">Yeniləmələri Yoxla</string> <string name="settings_check_update_title">Yeniləmələri Yoxla</string>
<string name="settings_check_update_summary">Axraplanda vaxtaşırı yeniləmələri yoxla.</string> <string name="settings_check_update_summary">Axraplanda vaxtaşırı yeniləmələri yoxla.</string>
<string name="settings_update_channel_title">Kanalı Yenilə</string> <string name="settings_update_channel_title">Kanalı Yenilə</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">Özəl</string> <string name="settings_update_custom">Özəl</string>
<string name="settings_update_custom_msg">Özəl URL daxil edin</string> <string name="settings_update_custom_msg">Özəl URL daxil edin</string>
<string name="settings_core_only_title">Magisk Yalnız Nüvə Modu</string> <string name="settings_core_only_title">Magisk Yalnız Nüvə Modu</string>
<string name="settings_core_only_summary">Yalnız nüvə xüsusiyyətlərini aç. MagiskSU və MagiskHide hələ də açıq qalacaq, amma əlavələr yüklənməyəcək.</string> <string name="settings_core_only_summary">Yalnız nüvə xüsusiyyətlərini aç. MagiskSU və MagiskHide hələ də açıq qalacaq, amma əlavələr yüklənməyəcək.</string>
<string name="settings_magiskhide_summary">Magisk\'i fərqli növdə aşkarlamalardan gizləyin.</string> <string name="settings_magiskhide_summary">Magisk\'i fərqli növdə aşkarlamalardan gizləyin.</string>
<string name="settings_hosts_title">Sistemsiz host\'lar</string> <string name="settings_hosts_title">Sistemsiz host\'lar</string>
<string name="settings_hosts_summary">Adblock tətbiqləri üçün Sistemsiz host dəstəyi.</string> <string name="settings_hosts_summary">Adblock tətbiqləri üçün Sistemsiz host dəstəyi.</string>
<string name="settings_hosts_toast">Sistemsiz host əlavəsi quraşdırıldı</string> <string name="settings_hosts_toast">Sistemsiz host əlavəsi quraşdırıldı</string>
<string name="settings_su_app_adb">Tətbiqlər və ADB</string> <string name="settings_su_app_adb">Tətbiqlər və ADB</string>
<string name="settings_su_app">Yalnız Tətbiqlər</string> <string name="settings_su_app">Yalnız Tətbiqlər</string>
<string name="settings_su_adb">Yalnız ADB</string> <string name="settings_su_adb">Yalnız ADB</string>
<string name="settings_su_disable">Qapalı</string> <string name="settings_su_disable">Qapalı</string>
<string name="settings_su_request_10">10 saniyə</string> <string name="settings_su_request_10">10 saniyə</string>
<string name="settings_su_request_15">15 saniyə</string> <string name="settings_su_request_15">15 saniyə</string>
<string name="settings_su_request_20">20 saniyə</string> <string name="settings_su_request_20">20 saniyə</string>
<string name="settings_su_request_30">30 saniyə</string> <string name="settings_su_request_30">30 saniyə</string>
<string name="settings_su_request_45">45 saniyə</string> <string name="settings_su_request_45">45 saniyə</string>
<string name="settings_su_request_60">60 saniyə</string> <string name="settings_su_request_60">60 saniyə</string>
<string name="superuser_access">Superuser İcazəsi</string> <string name="superuser_access">Superuser İcazəsi</string>
<string name="auto_response">Avtomatik Cavab</string> <string name="auto_response">Avtomatik Cavab</string>
<string name="request_timeout">İcazə Vaxtaşımı</string> <string name="request_timeout">İcazə Vaxtaşımı</string>
<string name="superuser_notification">Superuser Bildirişləri</string> <string name="superuser_notification">Superuser Bildirişləri</string>
<string name="request_timeout_summary">%1$d saniyə</string> <string name="request_timeout_summary">%1$d saniyə</string>
<string name="settings_su_reauth_title">Yüksəltmədən sonra Yenidən İdentifikasiya et</string> <string name="settings_su_reauth_title">Yüksəltmədən sonra Yenidən İdentifikasiya et</string>
<string name="settings_su_reauth_summary">Tətbiq yeniləmələridən sonra superuser icazələrini yenidən identifikasiya et</string> <string name="settings_su_reauth_summary">Tətbiq yeniləmələridən sonra superuser icazələrini yenidən identifikasiya et</string>
<string name="settings_su_fingerprint_title">Barmaq İzi İdentifikasiyasını</string> <string name="settings_su_fingerprint_title">Barmaq İzi İdentifikasiyasını</string>
<string name="settings_su_fingerprint_summary">Barmaq izi oxuyucunu superuser icazələri üçün işlət</string> <string name="settings_su_fingerprint_summary">Barmaq izi oxuyucunu superuser icazələri üçün işlət</string>
<string name="auth_fingerprint">Barmaq izini İdentifikasiya et</string> <string name="auth_fingerprint">Barmaq izini İdentifikasiya et</string>
<string name="multiuser_mode">Çox-istifadəçi modu</string> <string name="multiuser_mode">Çox-istifadəçi modu</string>
<string name="settings_owner_only">Yalnız cihaz sahibi</string> <string name="settings_owner_only">Yalnız cihaz sahibi</string>
<string name="settings_owner_manage">Cihaz sahibinin idarəçiliyində</string> <string name="settings_owner_manage">Cihaz sahibinin idarəçiliyində</string>
<string name="settings_user_independent">İstifadəçidən asılı olmayaraq</string> <string name="settings_user_independent">İstifadəçidən asılı olmayaraq</string>
<string name="owner_only_summary">Yalnız cihaz sahibinin root icazəsi var.</string> <string name="owner_only_summary">Yalnız cihaz sahibinin root icazəsi var.</string>
<string name="owner_manage_summary">Yalnız cihaz sahibi root icazələrini redaktə edə və icazə istəkləri qəbul edə bilər.</string> <string name="owner_manage_summary">Yalnız cihaz sahibi root icazələrini redaktə edə və icazə istəkləri qəbul edə bilər.</string>
<string name="user_indepenent_summary">Hər istifadəçinin ayrı root qaydaları var.</string> <string name="user_indepenent_summary">Hər istifadəçinin ayrı root qaydaları var.</string>
<string name="mount_namespace_mode">Namespace Modunu Qoş</string> <string name="mount_namespace_mode">Namespace Modunu Qoş</string>
<string name="settings_ns_global">Qlobal Namespace</string> <string name="settings_ns_global">Qlobal Namespace</string>
<string name="settings_ns_requester">Keçmə Namespace</string> <string name="settings_ns_requester">Keçmə Namespace</string>
<string name="settings_ns_isolate">Ayrılmış Namespace</string> <string name="settings_ns_isolate">Ayrılmış Namespace</string>
<string name="global_summary">Bütün root sessyaları qlobal qoşma namespace\'dən istifadə edir.</string> <string name="global_summary">Bütün root sessyaları qlobal qoşma namespace\'dən istifadə edir.</string>
<string name="requester_summary">Root sessyaları soruşulan namespace\'ləri birindən digərinə keçirəcək.</string> <string name="requester_summary">Root sessyaları soruşulan namespace\'ləri birindən digərinə keçirəcək.</string>
<string name="isolate_summary">Hər bir root sessyasının ayrılmış namespace\'i olacaq.</string> <string name="isolate_summary">Hər bir root sessyasının ayrılmış namespace\'i olacaq.</string>
<string name="android_o_not_support">Android 8.0+\'da dəstəklənmir.</string>
<string name="disable_fingerprint">Barmaq izi təyin edilməyib ya da dəstəklənmir.</string> <string name="disable_fingerprint">Barmaq izi təyin edilməyib ya da dəstəklənmir.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser Tələbi</string> <string name="su_request_title">Superuser Tələbi</string>
<string name="deny">Ləğv et</string> <string name="deny">Ləğv et</string>
<string name="prompt">Yönləndir</string> <string name="prompt">Yönləndir</string>
<string name="grant">Təmin et</string> <string name="grant">Təmin et</string>
<string name="su_warning">Cihazın tam icazəsi ilə təmin edin.\nƏmin deyilsinizsə ləğv edin!</string> <string name="su_warning">Cihazın tam icazəsi ilə təmin edin.\nƏmin deyilsinizsə ləğv edin!</string>
<string name="forever">Sonsuz</string> <string name="forever">Sonsuz</string>
<string name="once">Bir dəfəlik</string> <string name="once">Bir dəfəlik</string>
<string name="tenmin">10 dəq</string> <string name="tenmin">10 dəq</string>
<string name="twentymin">20 dəq</string> <string name="twentymin">20 dəq</string>
<string name="thirtymin">30 dəq</string> <string name="thirtymin">30 dəq</string>
<string name="sixtymin">60 dəq</string> <string name="sixtymin">60 dəq</string>
<string name="su_allow_toast">%1$s SuperUser icazəsi ilə təmin edildi</string> <string name="su_allow_toast">%1$s SuperUser icazəsi ilə təmin edildi</string>
<string name="su_deny_toast">%1$s SuperUser icazəsi ilə təmin edilmədi</string> <string name="su_deny_toast">%1$s SuperUser icazəsi ilə təmin edilmədi</string>
<string name="no_apps_found">Tətbiq yoxdur</string> <string name="no_apps_found">Tətbiq yoxdur</string>
<string name="su_snack_grant">%1$s üçün Superuser icazəsi verilib</string> <string name="su_snack_grant">%1$s üçün Superuser icazəsi verilib</string>
<string name="su_snack_deny">%1$s üçün Superuser icazəsi verilməyib</string> <string name="su_snack_deny">%1$s üçün Superuser icazəsi verilməyib</string>
<string name="su_snack_notif_on">%1$s üçün bildirişlər açıqdır</string> <string name="su_snack_notif_on">%1$s üçün bildirişlər açıqdır</string>
<string name="su_snack_notif_off">%1$s üçün bildirişlər bağlıdır</string> <string name="su_snack_notif_off">%1$s üçün bildirişlər bağlıdır</string>
<string name="su_snack_log_on">%1$s üçün giriş açıqdır</string> <string name="su_snack_log_on">%1$s üçün giriş açıqdır</string>
<string name="su_snack_log_off">%1$s üçün giriş bağlıdır</string> <string name="su_snack_log_off">%1$s üçün giriş bağlıdır</string>
<string name="su_revoke_title">Ləğv olunsun?</string> <string name="su_revoke_title">Ləğv olunsun?</string>
<string name="su_revoke_msg">%1$s üçün haqları ləğv etməyi təsdiq edirsiniz?</string> <string name="su_revoke_msg">%1$s üçün haqları ləğv etməyi təsdiq edirsiniz?</string>
<string name="toast">Tost</string> <string name="toast">Tost</string>
<string name="none">Heçnə</string> <string name="none">Heçnə</string>
<string name="auth_fail">İdentifikasiya xətası</string> <string name="auth_fail">İdentifikasiya xətası</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
<string name="target_uid">Hədəf UID: %1$d</string> <string name="target_uid">Hədəf UID: %1$d</string>
<string name="command">Komanda: %1$s</string> <string name="command">Komanda: %1$s</string>
<!-- MagiskHide --> <!-- MagiskHide -->
<string name="show_system_app">Sistem tətbiqlərini göstər</string> <string name="show_system_app">Sistem tətbiqlərini göstər</string>
</resources> </resources>

View File

@ -61,7 +61,6 @@
<string name="app_changelog">Списък с промени</string> <string name="app_changelog">Списък с промени</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Затваряне</string>
<string name="repo_install_title">Инсталиране на %1$s</string> <string name="repo_install_title">Инсталиране на %1$s</string>
<string name="repo_install_msg">Желаете ли да инсталирате %1$s сега?</string> <string name="repo_install_msg">Желаете ли да инсталирате %1$s сега?</string>
<string name="download">Изтегляне</string> <string name="download">Изтегляне</string>
@ -162,7 +161,6 @@
<string name="global_summary">Всички сесии с руут достъп използват глобалното именно пространство.</string> <string name="global_summary">Всички сесии с руут достъп използват глобалното именно пространство.</string>
<string name="requester_summary">Всички сесии с руут достъп наследяват именното пространство на запитващото приложение.</string> <string name="requester_summary">Всички сесии с руут достъп наследяват именното пространство на запитващото приложение.</string>
<string name="isolate_summary">Всички сесии с руут достъп имат собствени именни пространства.</string> <string name="isolate_summary">Всички сесии с руут достъп имат собствени именни пространства.</string>
<string name="android_o_not_support">Не поддържа Android 8.0+.</string>
<string name="disable_fingerprint">Не са добавени пръстови отпечатъци или устройството не поддържа тази функция.</string> <string name="disable_fingerprint">Не са добавени пръстови отпечатъци или устройството не поддържа тази функция.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -24,6 +24,7 @@
<string name="advanced_settings_title">Configuració avançada</string> <string name="advanced_settings_title">Configuració avançada</string>
<string name="keep_force_encryption">Mantenir el xifrat forçat</string> <string name="keep_force_encryption">Mantenir el xifrat forçat</string>
<string name="keep_dm_verity">Mantenir AVB 2.0/dm-verity</string> <string name="keep_dm_verity">Mantenir AVB 2.0/dm-verity</string>
<string name="recovery_mode">Mode de Recuperació</string>
<string name="current_installed">Instal·lada: %1$s</string> <string name="current_installed">Instal·lada: %1$s</string>
<string name="latest_version">Última: %1$s</string> <string name="latest_version">Última: %1$s</string>
<string name="uninstall">Desinstal·lar</string> <string name="uninstall">Desinstal·lar</string>
@ -75,19 +76,18 @@
<string name="manager_update_title">Actualització de Magisk Manager disponible!</string> <string name="manager_update_title">Actualització de Magisk Manager disponible!</string>
<!-- Installation --> <!-- Installation -->
<string name="manager_download_install">Prem per descarregar i instalar.</string> <string name="manager_download_install">Premi per baixar i instalar.</string>
<string name="download_zip_only">Descarrega només el ZIP</string> <string name="download_zip_only">Únicament baixa el ZIP</string>
<string name="direct_install">Instal·lació directa (Recomanat)</string> <string name="direct_install">Instal·lació directa (Recomanat)</string>
<string name="install_inactive_slot">Instal·la a la ranura inactiva (Després d\'una OTA)</string> <string name="install_inactive_slot">Instal·la a la ranura inactiva (Després d\'una OTA)</string>
<string name="install_inactive_slot_msg">El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar?</string> <string name="install_inactive_slot_msg">El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar?</string>
<string name="select_method">Sel·lecciona mètode</string> <string name="select_method">Sel·lecciona un mètode</string>
<string name="setup_title">Instal·lació addicional</string> <string name="setup_title">Instal·lació addicional</string>
<string name="select_patch_file">Sel·lecciona i arranja un arxiu</string> <string name="select_patch_file">Sel·lecciona i arranja un arxiu</string>
<string name="patch_file_msg">Sel·lecciona una imatge crua (*.img) o un ODIN tarfile (*.tar)</string> <string name="patch_file_msg">Sel·lecciona una imatge crua (*.img) o un ODIN tarfile (*.tar)</string>
<string name="reboot_delay_toast">Reinici en 5 segons…</string> <string name="reboot_delay_toast">Reinici en 5 segons…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Tancar</string>
<string name="repo_install_title">Instal·lar %1$s</string> <string name="repo_install_title">Instal·lar %1$s</string>
<string name="repo_install_msg">Vols instal·lar %1$s ara?</string> <string name="repo_install_msg">Vols instal·lar %1$s ara?</string>
<string name="download">Baixar</string> <string name="download">Baixar</string>
@ -110,23 +110,23 @@
<string name="restore_img_msg">Restaurant…</string> <string name="restore_img_msg">Restaurant…</string>
<string name="restore_done">Restauració feta!</string> <string name="restore_done">Restauració feta!</string>
<string name="restore_fail">La còpia de seguretat de Stock no existeix!</string> <string name="restore_fail">La còpia de seguretat de Stock no existeix!</string>
<string name="proprietary_title">Descarrega codi propietari</string> <string name="proprietary_title">Baixar codi propietari</string>
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nPot permetre que Magisk Manager descarregui una extensió que conté el GoogleApiClient per poder fer la comprobació de SafetyNet?</string> <string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nPot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprobació de SafetyNet?</string>
<string name="setup_fail">Instal·lació fallida.</string> <string name="setup_fail">Instal·lació fallida.</string>
<string name="env_fix_title">Es requereix instal·lació addicional</string> <string name="env_fix_title">Es requereix instal·lació addicional</string>
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es descarregarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instalació ara?</string> <string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instalació ara?</string>
<string name="setup_msg">S\'està executant la configuració de l\'entorn…</string> <string name="setup_msg">S\'està executant la configuració de l\'entorn…</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">General</string> <string name="settings_general_category">General</string>
<string name="settings_dark_theme_title">Tema obscur</string> <string name="settings_dark_theme_title">Tema fosc</string>
<string name="settings_dark_theme_summary">Habilitar el tema obscur</string> <string name="settings_dark_theme_summary">Habilitar el tema fosc</string>
<string name="settings_download_path_title">Directori de descàrrega</string> <string name="settings_download_path_title">Directori de baixades</string>
<string name="settings_download_path_message">Els arxius es desaràn a %1$s</string> <string name="settings_download_path_message">Els arxius es desaràn a %1$s</string>
<string name="settings_clear_cache_title">Netejar memòria cau del repositori</string> <string name="settings_clear_cache_title">Netejar memòria cau del repositori</string>
<string name="settings_clear_cache_summary">Neteja l\'informació en memòria cau per als repositoris en línia, força a l\'aplicació a actualitzar-se en línia.</string> <string name="settings_clear_cache_summary">Neteja l\'informació en memòria cau per als repositoris en línia, força a l\'aplicació a actualitzar-se en línia.</string>
<string name="settings_hide_manager_title">Amagar Magisk Manager</string> <string name="settings_hide_manager_title">Amagar Magisk Manager</string>
<string name="settings_hide_manager_summary">Re-empaquetar Magisk Manager amb un nom de paquet a l\'atzar</string> <string name="settings_hide_manager_summary">Reempaquetar Magisk Manager amb un nom de paquet a l\'atzar</string>
<string name="settings_restore_manager_title">Restaurar Magisk Manager</string> <string name="settings_restore_manager_title">Restaurar Magisk Manager</string>
<string name="settings_restore_manager_summary">Restaura Magisk Manager amb el nom de paquet original</string> <string name="settings_restore_manager_summary">Restaura Magisk Manager amb el nom de paquet original</string>
<string name="language">Idioma</string> <string name="language">Idioma</string>
@ -146,6 +146,10 @@
<string name="settings_hosts_summary">Suport per aplicacions tipus Adblock fora de la partició del sistema</string> <string name="settings_hosts_summary">Suport per aplicacions tipus Adblock fora de la partició del sistema</string>
<string name="settings_hosts_toast">Agregat el mòdul Systemless Hosts</string> <string name="settings_hosts_toast">Agregat el mòdul Systemless Hosts</string>
<string name="settings_app_name">Escriu el nom desitjat per l\'App</string>
<string name="settings_app_name_hint">Nou nom</string>
<string name="settings_app_name_helper">Es refarà l\'App amb aquest nom</string>
<string name="settings_app_name_error">Format invàl·lid</string>
<string name="settings_su_app_adb">Aplicacions y ADB</string> <string name="settings_su_app_adb">Aplicacions y ADB</string>
<string name="settings_su_app">Només aplicacions</string> <string name="settings_su_app">Només aplicacions</string>
<string name="settings_su_adb">Només ADB</string> <string name="settings_su_adb">Només ADB</string>
@ -161,8 +165,8 @@
<string name="request_timeout">Temps de petició</string> <string name="request_timeout">Temps de petició</string>
<string name="superuser_notification">Notificació de superusuari</string> <string name="superuser_notification">Notificació de superusuari</string>
<string name="request_timeout_summary">%1$d segons</string> <string name="request_timeout_summary">%1$d segons</string>
<string name="settings_su_reauth_title">Re-autenticació</string> <string name="settings_su_reauth_title">Demanar després d\'una actualització</string>
<string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació es actualitzada o reinstal·lada</string> <string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string>
<string name="settings_su_fingerprint_title">Autenticació per Empremta Dactilar</string> <string name="settings_su_fingerprint_title">Autenticació per Empremta Dactilar</string>
<string name="settings_su_fingerprint_summary">Utilitza el sensor d\'Empremta Dactilar per permetre les sol·licituds de superusuari</string> <string name="settings_su_fingerprint_summary">Utilitza el sensor d\'Empremta Dactilar per permetre les sol·licituds de superusuari</string>
<string name="auth_fingerprint">Autenticar Emprempta Digital</string> <string name="auth_fingerprint">Autenticar Emprempta Digital</string>
@ -182,7 +186,6 @@
<string name="global_summary">Totes les sessions d\'arrel utilitzen el suport Namespace Global</string> <string name="global_summary">Totes les sessions d\'arrel utilitzen el suport Namespace Global</string>
<string name="requester_summary">Les sessions d\'arrel heretaran les peticiones Namespace</string> <string name="requester_summary">Les sessions d\'arrel heretaran les peticiones Namespace</string>
<string name="isolate_summary">Totes les sessions d\'arrel tindran la seva pròpia Namespace</string> <string name="isolate_summary">Totes les sessions d\'arrel tindran la seva pròpia Namespace</string>
<string name="android_o_not_support">No es compatible amb Android 8.0+</string>
<string name="disable_fingerprint">No s\'han establert empremtes dactilars o no existeix el suport del dispositiu</string> <string name="disable_fingerprint">No s\'han establert empremtes dactilars o no existeix el suport del dispositiu</string>
<string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu.</string> <string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu.</string>

View File

@ -84,7 +84,6 @@
<string name="reboot_delay_toast">Restartování za 5 sekund…</string> <string name="reboot_delay_toast">Restartování za 5 sekund…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Zavřít</string>
<string name="repo_install_title">Instalovat %1$s</string> <string name="repo_install_title">Instalovat %1$s</string>
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string> <string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
<string name="download">Stáhnout</string> <string name="download">Stáhnout</string>
@ -175,7 +174,6 @@
<string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string> <string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string>
<string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string> <string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string>
<string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string> <string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string>
<string name="android_o_not_support">Nepodporuje Android 8.0+.</string>
<string name="disable_fingerprint">Nebyly nastaveny žádné otisky prstů ani žádná podpora zařízení.</string> <string name="disable_fingerprint">Nebyly nastaveny žádné otisky prstů ani žádná podpora zařízení.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -84,7 +84,6 @@
<string name="reboot_delay_toast">Neustart in 5 Sekunden…</string> <string name="reboot_delay_toast">Neustart in 5 Sekunden…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Schließen</string>
<string name="repo_install_title">Installiere %1$s</string> <string name="repo_install_title">Installiere %1$s</string>
<string name="repo_install_msg">Möchtest du %1$s installieren?</string> <string name="repo_install_msg">Möchtest du %1$s installieren?</string>
<string name="download">Herunterladen</string> <string name="download">Herunterladen</string>
@ -177,7 +176,6 @@
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string> <string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string>
<string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string> <string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string>
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string> <string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string>
<string name="android_o_not_support">Android 8.0+ wird nicht unterstützt</string>
<string name="disable_fingerprint">Keine Fingerabdrücke gespeichert oder keine Geräteunterstützung</string> <string name="disable_fingerprint">Keine Fingerabdrücke gespeichert oder keine Geräteunterstützung</string>
<!--Superuser--> <!--Superuser-->

View File

@ -62,7 +62,6 @@
<string name="app_changelog">Καταγραφή αλλαγών εφαρμογής</string> <string name="app_changelog">Καταγραφή αλλαγών εφαρμογής</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Κλείσιμο</string>
<string name="repo_install_title">Εγκατάσταση %1$s</string> <string name="repo_install_title">Εγκατάσταση %1$s</string>
<string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string> <string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string>
<string name="download">Λήψη</string> <string name="download">Λήψη</string>
@ -144,7 +143,6 @@
<string name="global_summary">Όλες οι συνεδρίες root χρησιμοποιούν τον καθολικό χώρο oνομάτων προσάρτησης</string> <string name="global_summary">Όλες οι συνεδρίες root χρησιμοποιούν τον καθολικό χώρο oνομάτων προσάρτησης</string>
<string name="requester_summary">Οι συνεδρίες root θα κληρονομούν το χώρο ονομάτων του αιτούντα τους</string> <string name="requester_summary">Οι συνεδρίες root θα κληρονομούν το χώρο ονομάτων του αιτούντα τους</string>
<string name="isolate_summary">Κάθε συνεδρία root θα έχει το δικό της απομονωμένο χώρο ονομάτων</string> <string name="isolate_summary">Κάθε συνεδρία root θα έχει το δικό της απομονωμένο χώρο ονομάτων</string>
<string name="android_o_not_support">Δεν υποστηρίζεται Android 8.0+</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Αίτημα υπερχρήστη</string> <string name="su_request_title">Αίτημα υπερχρήστη</string>

View File

@ -86,7 +86,6 @@
<string name="reboot_delay_toast">Reiniciando en 5 segundos…</string> <string name="reboot_delay_toast">Reiniciando en 5 segundos…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Cerrar</string>
<string name="repo_install_title">Instalar %1$s</string> <string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">¿Quieres instalar %1$s ahora?</string> <string name="repo_install_msg">¿Quieres instalar %1$s ahora?</string>
<string name="download">Descargar</string> <string name="download">Descargar</string>
@ -180,7 +179,6 @@
<string name="global_summary">Todas las sesiones de root utilizan el soporte Global Namespace</string> <string name="global_summary">Todas las sesiones de root utilizan el soporte Global Namespace</string>
<string name="requester_summary">Las sesiones de root heredarán las peticiones Namespace</string> <string name="requester_summary">Las sesiones de root heredarán las peticiones Namespace</string>
<string name="isolate_summary">Cada sesión root tendrá su propia Namespace</string> <string name="isolate_summary">Cada sesión root tendrá su propia Namespace</string>
<string name="android_o_not_support">No es compatible con Android 8.0+</string>
<string name="disable_fingerprint">No se establecieron huellas dactilares o no existe soporte del dispositivo</string> <string name="disable_fingerprint">No se establecieron huellas dactilares o no existe soporte del dispositivo</string>
<string name="settings_download_path_error">Error al crear la carpeta. Debe ser accesible desde el directorio raíz de almacenamiento y no debe ser un archivo.</string> <string name="settings_download_path_error">Error al crear la carpeta. Debe ser accesible desde el directorio raíz de almacenamiento y no debe ser un archivo.</string>

View File

@ -88,7 +88,6 @@
<string name="reboot_delay_toast">Taaskäivitamine 5 sekundi pärast…</string> <string name="reboot_delay_toast">Taaskäivitamine 5 sekundi pärast…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Sulge</string>
<string name="repo_install_title">Installi %1$s</string> <string name="repo_install_title">Installi %1$s</string>
<string name="repo_install_msg">Kas soovid kohe installida %1$s?</string> <string name="repo_install_msg">Kas soovid kohe installida %1$s?</string>
<string name="download">Allalaadimine</string> <string name="download">Allalaadimine</string>
@ -183,7 +182,6 @@
<string name="global_summary">Kõik juurkasutaja sessioonid kasutavad globaalset monteerimise nimeruumi.</string> <string name="global_summary">Kõik juurkasutaja sessioonid kasutavad globaalset monteerimise nimeruumi.</string>
<string name="requester_summary">Juurkasutaja sessioonid võtavad üle selle taotleja nimeruumi.</string> <string name="requester_summary">Juurkasutaja sessioonid võtavad üle selle taotleja nimeruumi.</string>
<string name="isolate_summary">Iga juurkasutaja sessioon saab oma isoleeritud nimeruumi.</string> <string name="isolate_summary">Iga juurkasutaja sessioon saab oma isoleeritud nimeruumi.</string>
<string name="android_o_not_support">Ei toeta Androidi versiooni 8.0+.</string>
<string name="disable_fingerprint">Sõrmejälgi pole määratud või seade pole toetatud.</string> <string name="disable_fingerprint">Sõrmejälgi pole määratud või seade pole toetatud.</string>
<string name="settings_download_path_error">Faili loomisel esines viga. See peab olema ligipääsetav mäluruumi juurkaustast ning ei tohi olla fail.</string> <string name="settings_download_path_error">Faili loomisel esines viga. See peab olema ligipääsetav mäluruumi juurkaustast ning ei tohi olla fail.</string>

View File

@ -88,7 +88,6 @@
<string name="reboot_delay_toast">Redémarrage dans 5 secondes…</string> <string name="reboot_delay_toast">Redémarrage dans 5 secondes…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Fermer</string>
<string name="repo_install_title">Installer %1$s</string> <string name="repo_install_title">Installer %1$s</string>
<string name="repo_install_msg">Voulezvous installer %1$s maintenant?</string> <string name="repo_install_msg">Voulezvous installer %1$s maintenant?</string>
<string name="download">Télécharger</string> <string name="download">Télécharger</string>
@ -183,7 +182,6 @@
<string name="global_summary">Toutes les sessions superutilisateur utilisent lespace de noms global du montage.</string> <string name="global_summary">Toutes les sessions superutilisateur utilisent lespace de noms global du montage.</string>
<string name="requester_summary">Les sessions superutilisateur hériteront de lespace de noms de leur demandeur.</string> <string name="requester_summary">Les sessions superutilisateur hériteront de lespace de noms de leur demandeur.</string>
<string name="isolate_summary">Chaque session superutilisateur aura son propre espace de noms isolé.</string> <string name="isolate_summary">Chaque session superutilisateur aura son propre espace de noms isolé.</string>
<string name="android_o_not_support">Android 8.0 et supérieurs ne sont pas pris en charge.</string>
<string name="disable_fingerprint">Aucune empreinte digitale na été définie ou le lecteur dempreinte nest pas pris en charge.</string> <string name="disable_fingerprint">Aucune empreinte digitale na été définie ou le lecteur dempreinte nest pas pris en charge.</string>
<string name="settings_download_path_error">Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string> <string name="settings_download_path_error">Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string>

View File

@ -0,0 +1,223 @@
<resources>
<!--Welcome Activity-->
<string name="modules">मॉड्यूल</string>
<string name="downloads">डाउनलोड</string>
<string name="superuser">उत्तम उपयोगकर्ता</string>
<string name="log">अभिलेख</string>
<string name="settings">सेटिंग्स</string>
<string name="install">स्थापित करें</string>
<string name="unsupport_magisk_title">असमर्थित Magisk संस्करण</string>
<string name="unsupport_magisk_message">Magisk Manager का यह संस्करण Magisk के v18.0 संस्करण से कम का समर्थन नहीं करता है.\n\nआप या तो खुद से Magisk को अपग्रेड करें, या फिर एप्लीकेशन को पुराने संस्करण पे डाउनग्रेड करें .</string>
<!--Status Fragment-->
<string name="magisk_version_error">Magisk स्थापित नहीं है</string>
<string name="checking_for_updates">अपडेट्स के लिए जांच हो रही है…</string>
<string name="invalid_update_channel">अमान्य अपडेट चैनल</string>
<string name="safetyNet_check_text">SafetyNet की जांच शुरू करें</string>
<string name="checking_safetyNet_status">SafetyNet के स्थिति की जाँच हो रही है…</string>
<string name="safetyNet_check_success">SafetyNet की जांच सफल हुई</string>
<string name="safetyNet_api_error">SafetyNet API त्रुटि</string>
<string name="safetyNet_res_invalid">अनुक्रिया अमान्य है.</string>
<string name="magisk_up_to_date">Magisk अप टू डेट है</string>
<string name="manager_up_to_date">Magisk Manager अप टू डेट है</string>
<string name="advanced_settings_title">एडवांस सेटिंग्स</string>
<string name="keep_force_encryption">बल एन्क्रिप्शन को बनाये रखें</string>
<string name="keep_dm_verity">AVB 2.0/dm-verity को बनाये रखें</string>
<string name="recovery_mode">रिकवरी मोड</string>
<string name="current_installed">स्थापित: %1$s</string>
<string name="latest_version">नवीनतम: %1$s</string>
<string name="uninstall">स्थापना रद्द करें</string>
<string name="uninstall_magisk_title">Magisk की स्थापना रद्द करें</string>
<string name="uninstall_magisk_msg">सभी मॉड्यूल अक्षम/हटा दिए जाएंगे. रुट और आपका संभावित रूप से एन्क्रिप्ट डाटा हटा दिया जाएगा.</string>
<string name="update">अपडेट करें</string>
<string name="core_only_enabled">(केवल मूल मोड समर्थकृत)</string>
<!--Module Fragment-->
<string name="no_info_provided">(कोई जानकारी प्रदान नहीं की गई)</string>
<string name="no_modules_found">कोई मॉड्यूल नहीं मिला</string>
<string name="update_file_created">मॉड्यूल अगले रिबूट पे अपडेट किया जाएगा !</string>
<string name="remove_file_created">मॉड्यूल अगले रिबूट पे हटाया जाएगा !</string>
<string name="remove_file_deleted">मॉड्यूल अगले रिबूट पे नहीं हटाया जाएगा !</string>
<string name="disable_file_created">मॉड्यूल अगले रिबूट पे निर्योग्य किया जाएगा !</string>
<string name="disable_file_removed">मॉड्यूल अगले रिबूट पे योग्य किया जाएगा !</string>
<string name="author">%1$s के द्वारा बनाया गया</string>
<string name="reboot_recovery">रिकवरी मोड में रिबूट करें</string>
<string name="reboot_bootloader">बूटलोडर में रिबूट करें</string>
<string name="reboot_download">डाउनलोड में रिबूट करें</string>
<string name="reboot_edl">EDL मोड में रिबूट करें</string>
<!--Repo Fragment-->
<string name="update_available">अपडेट उपलब्ध है</string>
<string name="installed">स्थापित</string>
<string name="not_installed">स्थापित नहीं है</string>
<string name="updated_on">%1$s को अपडेट किया गया</string>
<string name="sorting_order">छँटाई क्रम </string>
<string name="sort_by_name">नाम द्वारा छांटें</string>
<string name="sort_by_update">आखिरी अपडेट द्वारा छांटें</string>
<!--Log Fragment-->
<string name="menuSaveLog">अभिलेख सेव करें</string>
<string name="menuReload">पुनः लोड करें</string>
<string name="menuClearLog">अभिलेख साफ़ करें</string>
<string name="logs_cleared">अभिलेख सफलतापूर्वक साफ़ हो गया.</string>
<!--About Activity-->
<string name="app_changelog">परिवर्तन अभिलेख</string>
<!-- System Components, Notifications -->
<string name="update_channel">Magisk की अपडेट</string>
<string name="progress_channel">प्रगति सूचनाएँ</string>
<string name="download_complete">डाउनलोड सम्पन्न हुआ</string>
<string name="download_file_error">फ़ाइल डाउनलोड करने में त्रुटि</string>
<string name="download_open_parent">मूल फ़ोल्डर में दिखाएँ</string>
<string name="download_open_self">फाइल दिखाएँ</string>
<string name="magisk_update_title">Magisk की अपडेट उपलब्ध है!</string>
<string name="manager_update_title">Magisk Manager की अपडेट उपलब्ध है!</string>
<!-- Installation -->
<string name="manager_download_install">डाउनलोड और स्थापित करने के लिए दबाएँ.</string>
<string name="download_zip_only">खाली Zip डाउनलोड करें</string>
<string name="direct_install">सीधा स्थापित करें (अनुशंसित)</string>
<string name="install_inactive_slot">निष्क्रिय स्लॉट में स्थापित करें (OTA के बाद)</string>
<string name="install_inactive_slot_msg">आपके डिवाइस को रीबूट के बाद वर्तमान निष्क्रिय स्लॉट में बूट करने के लिए मजबूर किया जाएगा!\nOTA होने के बाद ही इस विकल्प का उपयोग करें.\nजारी रखें?</string>
<string name="select_method">विधि का चयन करें</string>
<string name="setup_title">अतिरिक्त सेटअप</string>
<string name="select_patch_file">एक फ़ाइल का चयन और पैच करें</string>
<string name="patch_file_msg">एक कच्ची इमेज चुनें (*.img) या एक ODIN tarfile (*.tar)</string>
<string name="reboot_delay_toast">5 सेकंड में रिबूट हो रहा है …</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">%1$s को स्थापित करें</string>
<string name="repo_install_msg">क्या आप %1$s को स्थापित करना चाहते हैं ?</string>
<string name="download">डाउनलोड</string>
<string name="reboot">रिबूट</string>
<string name="settings_reboot_toast">सेटिंग्स लागू करने के लिए रिबूट करें.</string>
<string name="release_notes">रिलीज नोट्स</string>
<string name="repo_cache_cleared">Repo cache साफ़ हो गया</string>
<string name="dtbo_patched_title">DTBO पैच कर दिया गया!</string>
<string name="dtbo_patched_reboot">Magisk Manager ने dtbo.img को पैच कर दिया. कृपया रिबूट करें.</string>
<string name="flashing">फ़्लैश हो रहा है…</string>
<string name="done">हो गया!</string>
<string name="failure">विफल हुआ</string>
<string name="hide_manager_title">Magisk Manager छुप रहा है…</string>
<string name="hide_manager_fail_toast">Magisk Manager छुपने में असफल रहा.</string>
<string name="open_link_failed_toast">लिंक खोलने के लिए कोई एप्लिकेशन नहीं मिला.</string>
<string name="warning">चेतावनी</string>
<string name="complete_uninstall">पूरी तरह से स्थापना रद्द करें</string>
<string name="restore_img">इमेजेज को पुनर्स्थापित करें</string>
<string name="restore_img_msg">वापस लाया जा रहा…</string>
<string name="restore_done">वापस ले आया गया!</string>
<string name="restore_fail">स्टॉक बैकअप मौजूद नहीं है!</string>
<string name="proprietary_title">मालिकाना कोड डाउनलोड करें</string>
<string name="proprietary_notice">Magisk Manager FOSS है और उस्में Google का मालिकाना SafetyNet API कोड शामिल नहीं है.\n\nक्या आप Magisk Manager को SafetyNet चेक के लिए एक्सटेंशन (GoogleApiClient शामिल) डाउनलोड करने की अनुमति देंगे ?</string>
<string name="setup_fail">सेटअप असफल हुआ.</string>
<string name="env_fix_title">अतिरिक्त सेटअप की आवश्यकता है</string>
<string name="env_fix_msg">ठीक से काम करने के लिए आपके डिवाइस को Magisk के लिए अतिरिक्त सेटअप की आवश्यकता है. यह Magisk सेटअप zip डाउनलोड करेगा, क्या आप आगे बढ़ना चाहते हैं?</string>
<string name="setup_msg">पर्यावरण सेटअप चल रहा है…</string>
<!--Settings Activity -->
<string name="settings_general_category">सामान्य</string>
<string name="settings_dark_theme_title">डार्क थीम</string>
<string name="settings_dark_theme_summary">डार्क थीम सक्षम करें.</string>
<string name="settings_download_path_title">डाउनलोड करने की जगह</string>
<string name="settings_download_path_message">%1$s में फाइल्स रखी जाएँगी</string>
<string name="settings_clear_cache_title">Repo Cache साफ़ करें</string>
<string name="settings_clear_cache_summary">ऑनलाइन Repo के लिए Cached जानकारी साफ़ करें. यह एप्लिकेशन को ऑनलाइन रिफ्रेश होने के लिए मजबूर करता है.</string>
<string name="settings_hide_manager_title">Magisk Manager को छुपाएं</string>
<string name="settings_hide_manager_summary">Magisk Manager को क्रमरहित नाम से फिर से पैकेज करें.</string>
<string name="settings_restore_manager_title">Magisk Manager को पुनर्स्थापित करें</string>
<string name="settings_restore_manager_summary">Magisk Manager को अपने मूल पैकेज नाम से पुनर्स्थापित करें</string>
<string name="language">भाषा</string>
<string name="system_default">(सिस्टम डिफ़ॉल्ट)</string>
<string name="settings_update">सेटिंग्स अपडेट करें</string>
<string name="settings_check_update_title">अपडेट के लिए जाँच करें</string>
<string name="settings_check_update_summary">समय-समय पर बैकग्राउंड में अपडेट की जांच करते रहें.</string>
<string name="settings_update_channel_title">अपडेट का चैनल</string>
<string name="settings_update_stable">स्थिर</string>
<string name="settings_update_beta">बीटा</string>
<string name="settings_update_custom">कस्टम</string>
<string name="settings_update_custom_msg">एक कस्टम URL डालें</string>
<string name="settings_core_only_title">Magisk का केवल मूल मोड</string>
<string name="settings_core_only_summary">केवल मुख्य विशेषताएं सक्षम करें. MagiskSU और MagiskHide अभी भी सक्षम रहेंगे, लेकिन कोई मॉड्यूल लोड नहीं किया जाएगा.</string>
<string name="settings_magiskhide_summary">पता लगाने के विभिन्न रूपों से Magisk को छुपाएं.</string>
<string name="settings_hosts_title">सिस्टमलेस होस्ट्स</string>
<string name="settings_hosts_summary">एडब्लॉक ऍप्लिकेशन्स के लिए सिस्टमलेस होस्ट्स का समर्थन</string>
<string name="settings_hosts_toast">सिस्टमलेस होस्ट्स का मॉड्यूल जोड़ दिया गया</string>
<string name="settings_su_app_adb">ऍप्लिकेशन्स और ADB</string>
<string name="settings_su_app">केवल ऍप्लिकेशन्स</string>
<string name="settings_su_adb">केवल ADB</string>
<string name="settings_su_disable">निर्योग्य</string>
<string name="settings_su_request_10">10 सेकंड्‌स</string>
<string name="settings_su_request_15">15 सेकंड्‌स</string>
<string name="settings_su_request_20">20 सेकंड्‌स</string>
<string name="settings_su_request_30">30 सेकंड्‌स</string>
<string name="settings_su_request_45">45 सेकंड्‌स</string>
<string name="settings_su_request_60">60 सेकंड्‌स</string>
<string name="superuser_access">उत्तम उपयोगकर्ता की पहुँच</string>
<string name="auto_response">स्वचालित प्रतिक्रिया</string>
<string name="request_timeout">निवेदन का समय समाप्त</string>
<string name="superuser_notification">उत्तम उपयोगकर्ता सूचना</string>
<string name="request_timeout_summary">%1$d सेकंड्‌स</string>
<string name="settings_su_reauth_title">अपग्रेड के बाद फिर से प्रमाणित करें</string>
<string name="settings_su_reauth_summary">एप्लीकेशन अपग्रेड होने के बाद उत्तम उपयोगकर्ता की अनुमतियों को फिर से प्रमाणित करें</string>
<string name="settings_su_fingerprint_title">फिंगरप्रिंट प्रमाणीकरण सक्षम करें</string>
<string name="settings_su_fingerprint_summary">उत्तम उपयोगकर्ता के अनुरोधों की अनुमति के लिए फिंगरप्रिंट स्कैनर का उपयोग करें</string>
<string name="auth_fingerprint">फिंगरप्रिंट को प्रमाणित करें</string>
<string name="multiuser_mode">बहु उपयोगकर्ता मोड</string>
<string name="settings_owner_only">केवल डिवाइस का मालिक</string>
<string name="settings_owner_manage">केवल डिवाइस के मालिक द्वौरा प्रभंदित</string>
<string name="settings_user_independent">उपयोगकर्ता स्वतंत्र</string>
<string name="owner_only_summary">केवल मालिक के पास ही रूट की पहुँच है.</string>
<string name="owner_manage_summary">केवल मालिक ही रूट की पहुँच का प्रबंधन कर सकता है और अनुरोध संकेत प्राप्त कर सकता है.</string>
<string name="user_indepenent_summary">प्रत्येक उपयोगकर्ता का अपना अलग रूट नियम होता है.</string>
<string name="mount_namespace_mode">माउंट नेमस्पेस मोड</string>
<string name="settings_ns_global">वैश्विक नेमस्पेस</string>
<string name="settings_ns_requester">नेमस्पेस को इन्हेरिट करें</string>
<string name="settings_ns_isolate">संगरोध नेमस्पेस</string>
<string name="global_summary">सभी रूट सत्र वैश्विक माउंट नेमस्पेस का उपयोग करते हैं.</string>
<string name="requester_summary">रूट सत्रों को उनके अनुरोधकर्ताओं के नेमस्पेस विरासत में मिलेंगे.</string>
<string name="isolate_summary">प्रत्येक रूट सत्र का अपना अलग नेमस्पेस होगा.</string>
<string name="disable_fingerprint">कोई फ़िंगरप्रिंट नहीं सेट किया गया या डिवाइस का समर्थन नहीं है.</string>
<string name="settings_download_path_error">फोल्डर बनाने में त्रुटि. यह स्टोरेज रूट डायरेक्टरी से एक्सेस होना चाहिए और फाइल नहीं होना चाहिए.</string>
<!--Superuser-->
<string name="su_request_title">उत्तम उपयोगकर्ता का अनुरोध</string>
<string name="deny">इंकार करें</string>
<string name="prompt">आदेश</string>
<string name="grant">अनुमति दें</string>
<string name="su_warning">यह आपके डिवाइस की पूरी पहुँच की अनुमति देगा. यदि आप सुनिश्चित नहीं हैं तो इंकार करें!</string>
<string name="forever">सदैव</string>
<string name="once">एक बार</string>
<string name="tenmin">10 मिनट</string>
<string name="twentymin">20 मिनट</string>
<string name="thirtymin">30 मिनट</string>
<string name="sixtymin">60 मिनट</string>
<string name="su_allow_toast">%1$s को उत्तम उपयोगकर्ता के अधिकार की अनुमति दी गई</string>
<string name="su_deny_toast">%1$s को उत्तम उपयोगकर्ता के अधिकार से इनकार किया गया</string>
<string name="no_apps_found">कोई एप्लीकेशन नहीं मिला</string>
<string name="su_snack_grant">%1$s को उत्तम उपयोगकर्ता के अधिकार की अनुमति है</string>
<string name="su_snack_deny">%1$s को उत्तम उपयोगकर्ता के अधिकार की अनुमति नहीं है</string>
<string name="su_snack_notif_on">%1$s की सूचनाएं सक्षम हैं</string>
<string name="su_snack_notif_off">%1$s की सूचनाएं अक्षम हैं</string>
<string name="su_snack_log_on">%1$s के लिए अभिलेख सक्षम है</string>
<string name="su_snack_log_off">%1$s के लिए अभिलेख अक्षम है</string>
<string name="su_revoke_title">वापस लें?</string>
<string name="su_revoke_msg">%1$s के अधिकारों को वापस लेने की पुष्टि करें?</string>
<string name="toast">पॉप-अप नोट</string>
<string name="none">कोई नहीं</string>
<string name="auth_fail">प्रमाणीकरण विफल हुआ</string>
<!--Superuser logs-->
<string name="pid">PID: %1$d</string>
<string name="target_uid">लक्ष्य UID: %1$d</string>
<string name="command">Command: %1$s</string>
<!-- MagiskHide -->
<string name="show_system_app">सिस्टम ऍप्लिकेशन्स दिखाएं</string>
</resources>

View File

@ -55,7 +55,6 @@
<string name="app_changelog">Popis izmjena aplikacije</string> <string name="app_changelog">Popis izmjena aplikacije</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Zatvori</string>
<string name="repo_install_title">Instaliraj %1$s</string> <string name="repo_install_title">Instaliraj %1$s</string>
<string name="repo_install_msg">Da li želite instalirati %1$s sada?</string> <string name="repo_install_msg">Da li želite instalirati %1$s sada?</string>
<string name="download">Preuzmi</string> <string name="download">Preuzmi</string>

View File

@ -86,7 +86,6 @@
<string name="reboot_delay_toast">Me-reboot dalam 5 detik…</string> <string name="reboot_delay_toast">Me-reboot dalam 5 detik…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Tutup</string>
<string name="repo_install_title">Pasang %1$s</string> <string name="repo_install_title">Pasang %1$s</string>
<string name="repo_install_msg">Apakah Anda ingin memasang %1$s sekarang?</string> <string name="repo_install_msg">Apakah Anda ingin memasang %1$s sekarang?</string>
<string name="download">Unduh</string> <string name="download">Unduh</string>
@ -181,7 +180,6 @@
<string name="global_summary">Semua sesi root menggunakan mount ruang nama global.</string> <string name="global_summary">Semua sesi root menggunakan mount ruang nama global.</string>
<string name="requester_summary">Sesi root akan mewarisi ruang nama peminta mereka.</string> <string name="requester_summary">Sesi root akan mewarisi ruang nama peminta mereka.</string>
<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>
<string name="android_o_not_support">Tidak mendukung Android 8.0+.</string>
<string name="disable_fingerprint">Tidak ada sidik jari diatur atau tidak ada dukungan perangkat.</string> <string name="disable_fingerprint">Tidak ada sidik jari diatur atau tidak ada dukungan perangkat.</string>
<string name="settings_download_path_error">Kesalahan membuat folder. Folder harus dapat diakses dari direktori penyimpanan root dan bukan merupakan file.</string> <string name="settings_download_path_error">Kesalahan membuat folder. Folder harus dapat diakses dari direktori penyimpanan root dan bukan merupakan file.</string>

View File

@ -86,7 +86,6 @@
<string name="reboot_delay_toast">Riavvio fra 5 secondi…</string> <string name="reboot_delay_toast">Riavvio fra 5 secondi…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Chiudi</string>
<string name="repo_install_title">Installazione di %1$s</string> <string name="repo_install_title">Installazione di %1$s</string>
<string name="repo_install_msg">Vuoi installare %1$s?</string> <string name="repo_install_msg">Vuoi installare %1$s?</string>
<string name="download">Download</string> <string name="download">Download</string>
@ -181,7 +180,6 @@
<string name="global_summary">Tutte le sessioni di root erediteranno il namespace globale.</string> <string name="global_summary">Tutte le sessioni di root erediteranno il namespace globale.</string>
<string name="requester_summary">Le sessioni di root erediteranno il namespace del loro richiedente.</string> <string name="requester_summary">Le sessioni di root erediteranno il namespace del loro richiedente.</string>
<string name="isolate_summary">Ogni sessione di root avrà il suo namespace isolato.</string> <string name="isolate_summary">Ogni sessione di root avrà il suo namespace isolato.</string>
<string name="android_o_not_support">Non è supportato da Android 8.0+.</string>
<string name="disable_fingerprint">Non è presente alcuna impronta o il dispositivo non è supportato.</string> <string name="disable_fingerprint">Non è presente alcuna impronta o il dispositivo non è supportato.</string>
<string name="settings_download_path_error">Errore durante la creazione della cartella. Deve essere accessibile dalla radice della memoria di archiviazione e non essere un file.</string> <string name="settings_download_path_error">Errore durante la creazione della cartella. Deve essere accessibile dalla radice della memoria di archiviazione e non essere un file.</string>

View File

@ -72,7 +72,6 @@
<string name="manager_update_title">Magisk Managerの更新があります</string> <string name="manager_update_title">Magisk Managerの更新があります</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">閉じる</string>
<string name="repo_install_title">%1$s をインストール</string> <string name="repo_install_title">%1$s をインストール</string>
<string name="repo_install_msg">%1$s をインストールしますか?</string> <string name="repo_install_msg">%1$s をインストールしますか?</string>
<string name="download">ダウンロード</string> <string name="download">ダウンロード</string>
@ -169,7 +168,6 @@
<string name="global_summary">すべてのrootセッションがグローバル名前空間を使用します</string> <string name="global_summary">すべてのrootセッションがグローバル名前空間を使用します</string>
<string name="requester_summary">rootセッションはリクエスト者の名前空間を継承します</string> <string name="requester_summary">rootセッションはリクエスト者の名前空間を継承します</string>
<string name="isolate_summary">rootセッション毎に分離された名前空間を使用します</string> <string name="isolate_summary">rootセッション毎に分離された名前空間を使用します</string>
<string name="android_o_not_support">Android 8.0以降では対応していません</string>
<string name="disable_fingerprint">指紋が登録されていないか、お使いの端末でサポートされていません。</string> <string name="disable_fingerprint">指紋が登録されていないか、お使いの端末でサポートされていません。</string>
<!--Superuser--> <!--Superuser-->

View File

@ -83,7 +83,6 @@
<string name="patch_file_msg">원시 이미지 (*.img) 또는 오딘 tar 파일 (*.tar) 선택</string> <string name="patch_file_msg">원시 이미지 (*.img) 또는 오딘 tar 파일 (*.tar) 선택</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">닫기</string>
<string name="repo_install_title">%1$s 설치</string> <string name="repo_install_title">%1$s 설치</string>
<string name="repo_install_msg">정말 %1$s을(를) 설치하시겠습니까?</string> <string name="repo_install_msg">정말 %1$s을(를) 설치하시겠습니까?</string>
<string name="download">다운로드</string> <string name="download">다운로드</string>
@ -176,7 +175,6 @@
<string name="global_summary">모든 루트 세션이 전역 마운트 이름공간을 사용합니다.</string> <string name="global_summary">모든 루트 세션이 전역 마운트 이름공간을 사용합니다.</string>
<string name="requester_summary">루트 세션은 요청자의 이름공간을 상속합니다.</string> <string name="requester_summary">루트 세션은 요청자의 이름공간을 상속합니다.</string>
<string name="isolate_summary">각각의 루트 세션은 자신만의 독립된 이름공간을 사용합니다.</string> <string name="isolate_summary">각각의 루트 세션은 자신만의 독립된 이름공간을 사용합니다.</string>
<string name="android_o_not_support">안드로이드 8.0 이상 버전에서 사용할 수 없습니다.</string>
<string name="disable_fingerprint">지문이 등록되지 않았거나 기기가 지문 인식을 지원하지 않습니다.</string> <string name="disable_fingerprint">지문이 등록되지 않았거나 기기가 지문 인식을 지원하지 않습니다.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -61,7 +61,6 @@
<string name="app_changelog">Pakeitimų sąrašas</string> <string name="app_changelog">Pakeitimų sąrašas</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Uždaryti</string>
<string name="repo_install_title">Instaliuoti %1$s</string> <string name="repo_install_title">Instaliuoti %1$s</string>
<string name="repo_install_msg">Ar jūs norite instaliuoti %1$s?</string> <string name="repo_install_msg">Ar jūs norite instaliuoti %1$s?</string>
<string name="download">Atsisiųsti</string> <string name="download">Atsisiųsti</string>
@ -156,7 +155,6 @@
<string name="global_summary">Visos root sesijos naudoja globalią vardų sritį</string> <string name="global_summary">Visos root sesijos naudoja globalią vardų sritį</string>
<string name="requester_summary">Root sesijos paveldi jos išprašytojo/s vardų sritį</string> <string name="requester_summary">Root sesijos paveldi jos išprašytojo/s vardų sritį</string>
<string name="isolate_summary">Kiekviena root sesija turi savo izoliuotą vardų sritį</string> <string name="isolate_summary">Kiekviena root sesija turi savo izoliuotą vardų sritį</string>
<string name="android_o_not_support">Įrenginiai su Android 8.0+ nepalaiko šio nustatymo</string>
<string name="disable_fingerprint">Jūsų įrenginyje nebuvo surasta pirštų antspaudų arba jūsų įrenginys neturi pirštų antspaudų skaitytuvo</string> <string name="disable_fingerprint">Jūsų įrenginyje nebuvo surasta pirštų antspaudų arba jūsų įrenginys neturi pirštų antspaudų skaitytuvo</string>
<!--Superuser--> <!--Superuser-->

View File

@ -84,7 +84,6 @@
<string name="reboot_delay_toast">Рестартирање за 5 секунди…</string> <string name="reboot_delay_toast">Рестартирање за 5 секунди…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Затвори</string>
<string name="repo_install_title">Инсталирај %1$s</string> <string name="repo_install_title">Инсталирај %1$s</string>
<string name="repo_install_msg">Дали сакате да го инсталирате %1$s сега?</string> <string name="repo_install_msg">Дали сакате да го инсталирате %1$s сега?</string>
<string name="download">Преземи</string> <string name="download">Преземи</string>
@ -175,7 +174,6 @@
<string name="global_summary">Сите рут сесии го користат глобалниот именски простор.</string> <string name="global_summary">Сите рут сесии го користат глобалниот именски простор.</string>
<string name="requester_summary">Рут сесиите ќе го наследат именскиот простор на нивниот барател.</string> <string name="requester_summary">Рут сесиите ќе го наследат именскиот простор на нивниот барател.</string>
<string name="isolate_summary">Секоја рут сесија ќе има свој изолиран именски простор.</string> <string name="isolate_summary">Секоја рут сесија ќе има свој изолиран именски простор.</string>
<string name="android_o_not_support">Не е поддржано на Android 8.0+.</string>
<string name="disable_fingerprint">Нема регистрирано отпечатоци од прсти или уредот не ја поддржува оваа функција.</string> <string name="disable_fingerprint">Нема регистрирано отпечатоци од прсти или уредот не ја поддржува оваа функција.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -69,7 +69,6 @@
<string name="manager_update_title">En Magisk Manager-oppdatering er tilgjengelig!</string> <string name="manager_update_title">En Magisk Manager-oppdatering er tilgjengelig!</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Lukk</string>
<string name="repo_install_title">Installer %1$s</string> <string name="repo_install_title">Installer %1$s</string>
<string name="repo_install_msg">Vil du installere %1$s nå?</string> <string name="repo_install_msg">Vil du installere %1$s nå?</string>
<string name="download">Last ned</string> <string name="download">Last ned</string>
@ -166,7 +165,6 @@
<string name="global_summary">Alle root-økter benytter det altdekkende monteringsnavnefeltet.</string> <string name="global_summary">Alle root-økter benytter det altdekkende monteringsnavnefeltet.</string>
<string name="requester_summary">Root-økter vil arve forespørrerens navnefelt.</string> <string name="requester_summary">Root-økter vil arve forespørrerens navnefelt.</string>
<string name="isolate_summary">Hver root-økt vil ha sitt eget isolerte navnefelt.</string> <string name="isolate_summary">Hver root-økt vil ha sitt eget isolerte navnefelt.</string>
<string name="android_o_not_support">Støtter ikke Android ≥8.0.</string>
<string name="disable_fingerprint">Ingen fingeravtrykk ble gitt, eller så støttes det ikke av enheten.</string> <string name="disable_fingerprint">Ingen fingeravtrykk ble gitt, eller så støttes det ikke av enheten.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -62,7 +62,6 @@
<string name="app_changelog">App\'s changelog</string> <string name="app_changelog">App\'s changelog</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Sluiten</string>
<string name="repo_install_title">%1$s installeren</string> <string name="repo_install_title">%1$s installeren</string>
<string name="repo_install_msg">Zeker weten %1$s installeren?</string> <string name="repo_install_msg">Zeker weten %1$s installeren?</string>
<string name="download">Downloaden</string> <string name="download">Downloaden</string>
@ -151,7 +150,6 @@
<string name="global_summary">Alle rootsessies gebruiken de globale naamruimte</string> <string name="global_summary">Alle rootsessies gebruiken de globale naamruimte</string>
<string name="requester_summary">Rootsessies verkrijgen de verzoeker\'s naamruimte</string> <string name="requester_summary">Rootsessies verkrijgen de verzoeker\'s naamruimte</string>
<string name="isolate_summary">Iedere rootsessie heeft een eigen geïsoleerde naamruimte</string> <string name="isolate_summary">Iedere rootsessie heeft een eigen geïsoleerde naamruimte</string>
<string name="android_o_not_support">Ondersteunt geen Android 8.0+</string>
<string name="disable_fingerprint">Geen vingerafdrukken ingesteld, of geen apparaatondersteuning</string> <string name="disable_fingerprint">Geen vingerafdrukken ingesteld, of geen apparaatondersteuning</string>
<!--Superuser--> <!--Superuser-->

View File

@ -87,7 +87,6 @@
<string name="reboot_delay_toast">Ponowne uruchomienie za 5 sekund…</string> <string name="reboot_delay_toast">Ponowne uruchomienie za 5 sekund…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Zamknij</string>
<string name="repo_install_title">Instaluj %1$s</string> <string name="repo_install_title">Instaluj %1$s</string>
<string name="repo_install_msg">Czy chcesz zainstalować %1$s ?</string> <string name="repo_install_msg">Czy chcesz zainstalować %1$s ?</string>
<string name="download">Pobierz</string> <string name="download">Pobierz</string>
@ -182,7 +181,6 @@
<string name="global_summary">Wszystkie sesje root za pomocą globalnej przestrzeni montowań nazw</string> <string name="global_summary">Wszystkie sesje root za pomocą globalnej przestrzeni montowań nazw</string>
<string name="requester_summary">Sesje Root będzie dziedziczyć prośby i nazwy</string> <string name="requester_summary">Sesje Root będzie dziedziczyć prośby i nazwy</string>
<string name="isolate_summary">W każdej sesji root będzie miał własną odosobnioną nazwę</string> <string name="isolate_summary">W każdej sesji root będzie miał własną odosobnioną nazwę</string>
<string name="android_o_not_support">Brak wsparcia dla Androida 8.0+</string>
<string name="disable_fingerprint">Nie ustawiono żadnych odcisków palców lub brak obsługi urządzenia</string> <string name="disable_fingerprint">Nie ustawiono żadnych odcisków palców lub brak obsługi urządzenia</string>
<string name="settings_download_path_error">Błąd podczas tworzenia folderu. Musi być dostępny z głównego katalogu pamięci i nie może być plikiem.</string> <string name="settings_download_path_error">Błąd podczas tworzenia folderu. Musi być dostępny z głównego katalogu pamięci i nie może być plikiem.</string>

View File

@ -63,7 +63,6 @@
<string name="app_changelog">Registro de mudanças</string> <string name="app_changelog">Registro de mudanças</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Fechar</string>
<string name="repo_install_title">Instalar %1$s</string> <string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">Instalar %1$s agora?</string> <string name="repo_install_msg">Instalar %1$s agora?</string>
<string name="download">Baixar</string> <string name="download">Baixar</string>
@ -153,7 +152,6 @@
<string name="global_summary">Todas as sessões root usam montagem de espaço de nome global</string> <string name="global_summary">Todas as sessões root usam montagem de espaço de nome global</string>
<string name="requester_summary">As sessões root herdarão espaço de nome de seu solicitante</string> <string name="requester_summary">As sessões root herdarão espaço de nome de seu solicitante</string>
<string name="isolate_summary">Cada sessão root terá seu próprio espaço de nome isolado</string> <string name="isolate_summary">Cada sessão root terá seu próprio espaço de nome isolado</string>
<string name="android_o_not_support">Não suporta Android 8.0+</string>
<string name="disable_fingerprint">Nenhuma impressão digital foi definida ou o dispostivo não tem suporte</string> <string name="disable_fingerprint">Nenhuma impressão digital foi definida ou o dispostivo não tem suporte</string>
<!--Superuser--> <!--Superuser-->

View File

@ -53,7 +53,6 @@
<string name="app_changelog">Lista de alterações da aplicação</string> <string name="app_changelog">Lista de alterações da aplicação</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Fechar</string>
<string name="repo_install_title">Instalar %1$s</string> <string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">Deseja instalar%1$s agora?</string> <string name="repo_install_msg">Deseja instalar%1$s agora?</string>
<string name="download">Transferir</string> <string name="download">Transferir</string>

View File

@ -87,7 +87,6 @@
<string name="reboot_delay_toast">Repornire în 5 secunde…</string> <string name="reboot_delay_toast">Repornire în 5 secunde…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Închide</string>
<string name="repo_install_title">Instalează %1$s</string> <string name="repo_install_title">Instalează %1$s</string>
<string name="repo_install_msg">Vrei să instalezi acum %1$s?</string> <string name="repo_install_msg">Vrei să instalezi acum %1$s?</string>
<string name="download">Descarcă</string> <string name="download">Descarcă</string>
@ -182,7 +181,6 @@
<string name="global_summary">Toate sesiunile de root folosesc spațiul de nume global.</string> <string name="global_summary">Toate sesiunile de root folosesc spațiul de nume global.</string>
<string name="requester_summary">Sesiunile de root vor moșteni spațiul de nume al solicitantului.</string> <string name="requester_summary">Sesiunile de root vor moșteni spațiul de nume al solicitantului.</string>
<string name="isolate_summary">Fiecare sesiune de root va avea propriul spațiu de nume izolat.</string> <string name="isolate_summary">Fiecare sesiune de root va avea propriul spațiu de nume izolat.</string>
<string name="android_o_not_support">Nu suportă Android 8.0+.</string>
<string name="disable_fingerprint">Nu au fost setate amprente sau scannerul de amprentă lipsește.</string> <string name="disable_fingerprint">Nu au fost setate amprente sau scannerul de amprentă lipsește.</string>
<string name="settings_download_path_error">Eroare la crearea dosarului. Acesta trebuie să fie accesibil din directorul rădăcină al stocării și nu trebuie să fie un fișier.</string> <string name="settings_download_path_error">Eroare la crearea dosarului. Acesta trebuie să fie accesibil din directorul rădăcină al stocării și nu trebuie să fie un fișier.</string>

View File

@ -18,18 +18,18 @@
<string name="checking_safetyNet_status">Проверка статуса SafetyNet…</string> <string name="checking_safetyNet_status">Проверка статуса SafetyNet…</string>
<string name="safetyNet_check_success">Результат проверки SafetyNet</string> <string name="safetyNet_check_success">Результат проверки SafetyNet</string>
<string name="safetyNet_api_error">Ошибка SafetyNet API</string> <string name="safetyNet_api_error">Ошибка SafetyNet API</string>
<string name="safetyNet_res_invalid">Некорректный ответ.</string> <string name="safetyNet_res_invalid">Некорректный ответ</string>
<string name="magisk_up_to_date">Magisk актуален</string> <string name="magisk_up_to_date">Magisk актуален</string>
<string name="manager_up_to_date">Magisk Manager актуален</string> <string name="manager_up_to_date">Magisk Manager актуален</string>
<string name="advanced_settings_title">Расширенные опции</string> <string name="advanced_settings_title">Расширенные опции</string>
<string name="keep_force_encryption">Сохранить принудительное шифрование</string> <string name="keep_force_encryption">Не отключать шифрование /data</string>
<string name="keep_dm_verity">Сохранить AVB 2.0/dm-verity</string> <string name="keep_dm_verity">Не отключать AVB 2.0/dm-verity</string>
<string name="recovery_mode">Режим Recovery</string> <string name="recovery_mode">Режим установки в recovery</string>
<string name="current_installed">Установлена: %1$s</string> <string name="current_installed">Установлен: %1$s</string>
<string name="latest_version">Последняя: %1$s</string> <string name="latest_version">Последний: %1$s</string>
<string name="uninstall">Удаление</string> <string name="uninstall">Удаление</string>
<string name="uninstall_magisk_title">Удаление Magisk</string> <string name="uninstall_magisk_title">Удаление Magisk</string>
<string name="uninstall_magisk_msg">Все модули будут отключены/удалены. Root-права будут удалены. Шифрование будет активировано.</string> <string name="uninstall_magisk_msg">Все модули будут отключены/удалены!\nRoot-права будут удалены!\nШифрование будет активировано!</string>
<string name="update">Обновить</string> <string name="update">Обновить</string>
<string name="core_only_enabled">(Активирован режим Magisk Core)</string> <string name="core_only_enabled">(Активирован режим Magisk Core)</string>
@ -76,24 +76,23 @@
<string name="manager_update_title">Доступно обновление Magisk Manager!</string> <string name="manager_update_title">Доступно обновление Magisk Manager!</string>
<!-- Installation --> <!-- Installation -->
<string name="manager_download_install">Нажмите, чтобы загрузить и установить.</string> <string name="manager_download_install">Нажмите, чтобы загрузить и установить</string>
<string name="download_zip_only">Загрузка установочного ZIP</string> <string name="download_zip_only">Только загрузка ZIP</string>
<string name="direct_install">Прямая установка (Рекомендуется)</string> <string name="direct_install">Прямая установка (Рекомендуется)</string>
<string name="install_inactive_slot">Установка в неактивный слот (После OTA)</string> <string name="install_inactive_slot">Установка во второй слот (OTA)</string>
<string name="install_inactive_slot_msg">Ваше устройство будет принудительно перезагружено в неактивный слот!\nИспользуйте эту опцию только при установке OTA.\nПродолжить?</string> <string name="install_inactive_slot_msg">Ваше устройство будет принудительно перезагружено в неактивный (противоположный) слот!\nИспользуйте эту опцию только при интеграции после OTA.\nПродолжить?</string>
<string name="select_method">Выбор способа</string> <string name="select_method">Выбор способа</string>
<string name="setup_title">Дополнительная установка</string> <string name="setup_title">Дополнительная установка</string>
<string name="select_patch_file">Выбрать и пропатчить файл</string> <string name="select_patch_file">Вручную пропатчить образ</string>
<string name="patch_file_msg">Выберите файл образа (*.img) или архив ODIN (*.tar)</string> <string name="patch_file_msg">Выберите файл образа (*.img) или архив ODIN (*.tar)</string>
<string name="reboot_delay_toast">Перезагрузка через 5 секунд…</string> <string name="reboot_delay_toast">Перезагрузка через 5 секунд…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Закрыть</string>
<string name="repo_install_title">Установка %1$s</string> <string name="repo_install_title">Установка %1$s</string>
<string name="repo_install_msg">Установить %1$s ?</string> <string name="repo_install_msg">Установить %1$s ?</string>
<string name="download">Скачать</string> <string name="download">Скачать</string>
<string name="reboot">Перезагрузка</string> <string name="reboot">Перезагрузка</string>
<string name="settings_reboot_toast">Для применения настроек перезагрузите устройство.</string> <string name="settings_reboot_toast">Для применения настроек перезагрузите устройство</string>
<string name="release_notes">О версии</string> <string name="release_notes">О версии</string>
<string name="repo_cache_cleared">Кэш репозитория очищен</string> <string name="repo_cache_cleared">Кэш репозитория очищен</string>
@ -102,9 +101,9 @@
<string name="flashing">Прошивка…</string> <string name="flashing">Прошивка…</string>
<string name="done">Завершено!</string> <string name="done">Завершено!</string>
<string name="failure">Ошибка</string> <string name="failure">Ошибка</string>
<string name="hide_manager_title">Маскировка Magisk Manager…</string> <string name="hide_manager_title">Скрытие Magisk Manager…</string>
<string name="hide_manager_fail_toast">Не удалось пересобрать Magisk Manager</string> <string name="hide_manager_fail_toast">Не удалось пересобрать Magisk Manager</string>
<string name="open_link_failed_toast">Не найдено приложений для открытия ссылки.</string> <string name="open_link_failed_toast">Не найдено приложений для открытия ссылки</string>
<string name="warning">Предупреждение</string> <string name="warning">Предупреждение</string>
<string name="complete_uninstall">Полное удаление</string> <string name="complete_uninstall">Полное удаление</string>
<string name="restore_img">Восстановить разделы</string> <string name="restore_img">Восстановить разделы</string>
@ -112,8 +111,8 @@
<string name="restore_done">Восстановление завершено!</string> <string name="restore_done">Восстановление завершено!</string>
<string name="restore_fail">Резервная копия отсутствует!</string> <string name="restore_fail">Резервная копия отсутствует!</string>
<string name="proprietary_title">Загрузка SafetyNet</string> <string name="proprietary_title">Загрузка SafetyNet</string>
<string name="proprietary_notice">Magisk Manager — свободно распространяемый продукт, он не содержит собственный код SafetyNet API от Google.\n\nРазрешить Magisk Manager загрузить расширение для проверки SafetyNet? (содержит GoogleApiClient)</string> <string name="proprietary_notice">Magisk Manager — проект с открытым исходным кодом и не содержит проприетарный код SafetyNet API от Google.\n\nРазрешить Magisk Manager загрузить расширение для проверки SafetyNet? (содержит GoogleApiClient)</string>
<string name="setup_fail">Ошибка установки.</string> <string name="setup_fail">Ошибка установки</string>
<string name="env_fix_title">Требуется дополнительная установка</string> <string name="env_fix_title">Требуется дополнительная установка</string>
<string name="env_fix_msg">Вашему устройству требуется дополнительная установка Magisk для корректной работы. Будет загружен установочный ZIP Magisk, продолжить?</string> <string name="env_fix_msg">Вашему устройству требуется дополнительная установка Magisk для корректной работы. Будет загружен установочный ZIP Magisk, продолжить?</string>
<string name="setup_msg">Настройка рабочей среды…</string> <string name="setup_msg">Настройка рабочей среды…</string>
@ -121,32 +120,36 @@
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">Основные</string> <string name="settings_general_category">Основные</string>
<string name="settings_dark_theme_title">Тёмная тема</string> <string name="settings_dark_theme_title">Тёмная тема</string>
<string name="settings_dark_theme_summary">Включить тёмное оформление.</string> <string name="settings_dark_theme_summary">Включить тёмное оформление</string>
<string name="settings_download_path_title">Папка загрузки</string> <string name="settings_download_path_title">Папка загрузки</string>
<string name="settings_download_path_message">Файлы будут загружаться в %1$s</string> <string name="settings_download_path_message">Файлы будут загружаться в %1$s</string>
<string name="settings_clear_cache_title">Очистка кэша репозитория</string> <string name="settings_clear_cache_title">Очистка кэша репозитория</string>
<string name="settings_clear_cache_summary">Очистить кэш репозитория. Будет загружен заново.</string> <string name="settings_clear_cache_summary">Очистить кэш репозитория. Будет загружен заново</string>
<string name="settings_hide_manager_title">Маскировка Magisk Manager</string> <string name="settings_hide_manager_title">Скрытие Magisk Manager</string>
<string name="settings_hide_manager_summary">Пересобрать Magisk Manager со случайным именем пакета.</string> <string name="settings_hide_manager_summary">Пересобрать Magisk Manager со случайным названием и именем пакета</string>
<string name="settings_restore_manager_title">Восстановление Magisk Manager</string> <string name="settings_restore_manager_title">Восстановление Magisk Manager</string>
<string name="settings_restore_manager_summary">Восстановить Magisk Manager с исходным именем пакета.</string> <string name="settings_restore_manager_summary">Восстановить Magisk Manager с исходным названием и именем пакета</string>
<string name="language">Язык</string> <string name="language">Язык</string>
<string name="system_default">По умолчанию (Системный)</string> <string name="system_default">По умолчанию (Системный)</string>
<string name="settings_update">Настройки обновлений</string> <string name="settings_update">Настройки обновлений</string>
<string name="settings_check_update_title">Проверка обновлений</string> <string name="settings_check_update_title">Проверка обновлений</string>
<string name="settings_check_update_summary">Периодически проверять наличие обновлений в фоновом режиме.</string> <string name="settings_check_update_summary">Периодически проверять наличие обновлений в фоновом режиме</string>
<string name="settings_update_channel_title">Источник обновлений</string> <string name="settings_update_channel_title">Источник обновлений</string>
<string name="settings_update_stable">Стабильный канал</string> <string name="settings_update_stable">Стабильный канал</string>
<string name="settings_update_beta">Beta канал</string> <string name="settings_update_beta">Beta канал</string>
<string name="settings_update_custom">Сторонний канал</string> <string name="settings_update_custom">Сторонний канал</string>
<string name="settings_update_custom_msg">Укажите ссылку</string> <string name="settings_update_custom_msg">Укажите ссылку</string>
<string name="settings_core_only_title">Magisk Core</string> <string name="settings_core_only_title">Magisk Core</string>
<string name="settings_core_only_summary">Активировать только основные возможности. Модули не будут загружены. MagiskSU и Magisk Hide останутся активными.</string> <string name="settings_core_only_summary">Активировать только основные возможности. Модули не будут загружены. MagiskSU и Magisk Hide останутся активными</string>
<string name="settings_magiskhide_summary">Скрыть Magisk от различных обнаружений.</string> <string name="settings_magiskhide_summary">Скрывать Magisk от различных обнаружений</string>
<string name="settings_hosts_title">Внесистемные хосты</string> <string name="settings_hosts_title">Внесистемные хосты</string>
<string name="settings_hosts_summary">Поддержка внесистемных хостов для приложений, блокирующих рекламу.</string> <string name="settings_hosts_summary">Поддержка внесистемных хостов для приложений, блокирующих рекламу</string>
<string name="settings_hosts_toast">Добавлен модуль внесистемных хостов</string> <string name="settings_hosts_toast">Добавлен модуль внесистемных хостов</string>
<string name="settings_app_name">Укажите имя приложения</string>
<string name="settings_app_name_hint">Новое имя</string>
<string name="settings_app_name_helper">Приложение будет пересобрано с этим именем</string>
<string name="settings_app_name_error">Некорректный формат</string>
<string name="settings_su_app_adb">Приложения и ADB</string> <string name="settings_su_app_adb">Приложения и ADB</string>
<string name="settings_su_app">Только приложения</string> <string name="settings_su_app">Только приложения</string>
<string name="settings_su_adb">Только ADB</string> <string name="settings_su_adb">Только ADB</string>
@ -163,18 +166,18 @@
<string name="superuser_notification">Уведомления суперпользователя</string> <string name="superuser_notification">Уведомления суперпользователя</string>
<string name="request_timeout_summary">%1$d секунд</string> <string name="request_timeout_summary">%1$d секунд</string>
<string name="settings_su_reauth_title">Повторная аутентификация</string> <string name="settings_su_reauth_title">Повторная аутентификация</string>
<string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений.</string> <string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений</string>
<string name="settings_su_fingerprint_title">Биометрическая аутентификация</string> <string name="settings_su_fingerprint_title">Биометрическая аутентификация</string>
<string name="settings_su_fingerprint_summary">Использовать сканер отпечатков пальцев для запросов прав суперпользователя.</string> <string name="settings_su_fingerprint_summary">Использовать сканер отпечатков пальцев для запросов прав суперпользователя</string>
<string name="auth_fingerprint">Аутентифицировать отпечаток пальца</string> <string name="auth_fingerprint">Подтвердите отпечаток пальца</string>
<string name="multiuser_mode">Многопользовательский режим</string> <string name="multiuser_mode">Многопользовательский режим</string>
<string name="settings_owner_only">Только владелец</string> <string name="settings_owner_only">Только владелец</string>
<string name="settings_owner_manage">Регулировка владельцем</string> <string name="settings_owner_manage">Регулировка владельцем</string>
<string name="settings_user_independent">Правила пользователей</string> <string name="settings_user_independent">Правила пользователей</string>
<string name="owner_only_summary">Только владелец имеет Root-доступ.</string> <string name="owner_only_summary">Только владелец имеет Root-доступ</string>
<string name="owner_manage_summary">Только владелец управляет Root-доступом и обрабатывает запросы.</string> <string name="owner_manage_summary">Только владелец управляет Root-доступом и обрабатывает запросы</string>
<string name="user_indepenent_summary">Каждый пользователь имеет свои собственные правила Root-доступа.</string> <string name="user_indepenent_summary">Каждый пользователь имеет свои собственные правила Root-доступа</string>
<string name="mount_namespace_mode">Настройка пространств имён</string> <string name="mount_namespace_mode">Настройка пространств имён</string>
<string name="settings_ns_global">Общее пространство имён</string> <string name="settings_ns_global">Общее пространство имён</string>
@ -183,18 +186,17 @@
<string name="global_summary">Сессии суперпользователя используют общее пространство имён</string> <string name="global_summary">Сессии суперпользователя используют общее пространство имён</string>
<string name="requester_summary">Сессии суперпользователя наследуют пространство имён запрашивающего</string> <string name="requester_summary">Сессии суперпользователя наследуют пространство имён запрашивающего</string>
<string name="isolate_summary">Сессии суперпользователя используют изолированные пространства имён</string> <string name="isolate_summary">Сессии суперпользователя используют изолированные пространства имён</string>
<string name="android_o_not_support">Не поддерживается в Android 8.0+</string>
<string name="disable_fingerprint">Не поддерживается устройством или не заданы отпечатки</string> <string name="disable_fingerprint">Не поддерживается устройством или не заданы отпечатки</string>
<string name="settings_download_path_error">Ошибка создания папки. Она должна быть доступна из корневой директории хранилища и не должна быть файлом.</string> <string name="settings_download_path_error">Ошибка создания папки. Она должна быть доступна из корневой директории хранилища и не должна быть файлом.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Запрос прав суперпользователя</string> <string name="su_request_title">Запрос прав суперпользователя</string>
<string name="deny">Отказать</string> <string name="deny">Запретить</string>
<string name="prompt">Запрос</string> <string name="prompt">Запрос</string>
<string name="grant">Предоставить</string> <string name="grant">Разрешить</string>
<string name="su_warning">Предоставить полный доступ к устройству.\nЕсли не уверены - отклоните данное действие!</string> <string name="su_warning">Разрешить полный доступ к устройству?\nЕсли не уверены - отклоните данное действие!</string>
<string name="forever">Навсегда</string> <string name="forever">Навсегда</string>
<string name="once">Сейчас</string> <string name="once">Единожды</string>
<string name="tenmin">10 мин.</string> <string name="tenmin">10 мин.</string>
<string name="twentymin">20 мин.</string> <string name="twentymin">20 мин.</string>
<string name="thirtymin">30 мин.</string> <string name="thirtymin">30 мин.</string>
@ -220,6 +222,6 @@
<string name="command">Команда: %1$s</string> <string name="command">Команда: %1$s</string>
<!-- MagiskHide --> <!-- MagiskHide -->
<string name="show_system_app">Показать системные приложения</string> <string name="show_system_app">Системные приложения</string>
</resources> </resources>

View File

@ -72,7 +72,6 @@
<string name="manager_update_title">Je dostupná aktualizácia Magisk Manager!</string> <string name="manager_update_title">Je dostupná aktualizácia Magisk Manager!</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Zavrieť</string>
<string name="repo_install_title">Nainštalovať %1$s</string> <string name="repo_install_title">Nainštalovať %1$s</string>
<string name="repo_install_msg">Chcete teraz nainštalovať %1$s?</string> <string name="repo_install_msg">Chcete teraz nainštalovať %1$s?</string>
<string name="download">Stiahnuť</string> <string name="download">Stiahnuť</string>
@ -167,7 +166,6 @@
<string name="global_summary">Všetky relácie root použijú globálne mount namespace</string> <string name="global_summary">Všetky relácie root použijú globálne mount namespace</string>
<string name="requester_summary">Relácie root zdedia namespace od požadovateľa</string> <string name="requester_summary">Relácie root zdedia namespace od požadovateľa</string>
<string name="isolate_summary">Každá relácia root bude mať vlastný izolovaný namespace</string> <string name="isolate_summary">Každá relácia root bude mať vlastný izolovaný namespace</string>
<string name="android_o_not_support">Nepodporuje Android 8.0+</string>
<string name="disable_fingerprint">Neboli odoslané žiadne odtlačky prsta alebo ich zariadenie nepodporuje</string> <string name="disable_fingerprint">Neboli odoslané žiadne odtlačky prsta alebo ich zariadenie nepodporuje</string>
<!--Superuser--> <!--Superuser-->

View File

@ -55,7 +55,6 @@
<string name="app_changelog">Дневник промена апликације</string> <string name="app_changelog">Дневник промена апликације</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Затвори</string>
<string name="repo_install_title">Инсталирај %1$s</string> <string name="repo_install_title">Инсталирај %1$s</string>
<string name="repo_install_msg">Да ли желите да инсталирате %1$s?</string> <string name="repo_install_msg">Да ли желите да инсталирате %1$s?</string>
<string name="download">Преузми</string> <string name="download">Преузми</string>

View File

@ -53,7 +53,6 @@
<string name="app_changelog">Ändringslogg</string> <string name="app_changelog">Ändringslogg</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Stäng</string>
<string name="repo_install_title">Installera %1$s</string> <string name="repo_install_title">Installera %1$s</string>
<string name="repo_install_msg">Vill du installera %1$s ?</string> <string name="repo_install_msg">Vill du installera %1$s ?</string>
<string name="download">Ladda ner</string> <string name="download">Ladda ner</string>
@ -62,7 +61,7 @@
<string name="settings_reboot_toast">Starta om för att tillämpa inställningar</string> <string name="settings_reboot_toast">Starta om för att tillämpa inställningar</string>
<string name="release_notes">Release notes</string> <string name="release_notes">Release notes</string>
<string name="repo_cache_cleared">Repo-cache rensad</string> <string name="repo_cache_cleared">Repo-cache rensad</string>
<string name="manager_update_title">En uppdatering av Magisk maneger finns tillgänglig!</string> <string name="manager_update_title">En uppdatering av Magisk Manager finns tillgänglig!</string>
<string name="manager_download_install">Tryck för att ladda ner och installera</string> <string name="manager_download_install">Tryck för att ladda ner och installera</string>
<string name="update_channel">Magiska uppdateringar</string> <string name="update_channel">Magiska uppdateringar</string>
<string name="download_file_error">Fel vid nerladdning av fil</string> <string name="download_file_error">Fel vid nerladdning av fil</string>

View File

@ -69,7 +69,6 @@
<string name="manager_update_title">มีการอัพเดต Magisk Manager!</string> <string name="manager_update_title">มีการอัพเดต Magisk Manager!</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">ปิด</string>
<string name="repo_install_title">ติดตั้ง %1$s</string> <string name="repo_install_title">ติดตั้ง %1$s</string>
<string name="repo_install_msg">ต้องการติดตั้ง %1$s ตอนนี้หรือไม่?</string> <string name="repo_install_msg">ต้องการติดตั้ง %1$s ตอนนี้หรือไม่?</string>
<string name="download">ดาวน์โหลด</string> <string name="download">ดาวน์โหลด</string>
@ -166,7 +165,6 @@
<string name="global_summary">All root sessions use the global mount namespace.</string> <string name="global_summary">All root sessions use the global mount namespace.</string>
<string name="requester_summary">Root sessions will inherit their requester\'s namespace.</string> <string name="requester_summary">Root sessions will inherit their requester\'s namespace.</string>
<string name="isolate_summary">Each root session will have its own isolated namespace.</string> <string name="isolate_summary">Each root session will have its own isolated namespace.</string>
<string name="android_o_not_support">ไม่รองรับ Android 8.0 ขึ้นไป</string>
<string name="disable_fingerprint">ไม่มีลายนิ้วมือหรืออุปกรณ์แสกน</string> <string name="disable_fingerprint">ไม่มีลายนิ้วมือหรืออุปกรณ์แสกน</string>
<!--Superuser--> <!--Superuser-->

View File

@ -88,7 +88,6 @@
<string name="reboot_delay_toast">5 saniye içinde yeniden başlatılacak...</string> <string name="reboot_delay_toast">5 saniye içinde yeniden başlatılacak...</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Kapat</string>
<string name="repo_install_title">%1$s yükle</string> <string name="repo_install_title">%1$s yükle</string>
<string name="repo_install_msg">%1$s yüklensin mi?</string> <string name="repo_install_msg">%1$s yüklensin mi?</string>
<string name="download">İndir</string> <string name="download">İndir</string>
@ -147,6 +146,10 @@
<string name="settings_hosts_summary">Reklam engelleme uygulamaları için sistemsiz host desteği</string> <string name="settings_hosts_summary">Reklam engelleme uygulamaları için sistemsiz host desteği</string>
<string name="settings_hosts_toast">Sistemsiz host modülü eklendi</string> <string name="settings_hosts_toast">Sistemsiz host modülü eklendi</string>
<string name="settings_app_name">İstediğiniz uygulama adını yazın</string>
<string name="settings_app_name_hint">Yeni ad</string>
<string name="settings_app_name_helper">Uygulama bu ad ile yeniden paketlenecek</string>
<string name="settings_app_name_error">Geçersiz format</string>
<string name="settings_su_app_adb">Uygulamalar ve ADB</string> <string name="settings_su_app_adb">Uygulamalar ve ADB</string>
<string name="settings_su_app">Sadece uygulamalar</string> <string name="settings_su_app">Sadece uygulamalar</string>
<string name="settings_su_adb">Sadece ADB</string> <string name="settings_su_adb">Sadece ADB</string>
@ -183,7 +186,6 @@
<string name="global_summary">Tüm kök oturumları genel bağlama ad alanını kullanır</string> <string name="global_summary">Tüm kök oturumları genel bağlama ad alanını kullanır</string>
<string name="requester_summary">Kök oturumları, istekte bulunanın ad alanını devralır</string> <string name="requester_summary">Kök oturumları, istekte bulunanın ad alanını devralır</string>
<string name="isolate_summary">Her bir kök oturumunun kendi izole ad alanı olacaktır</string> <string name="isolate_summary">Her bir kök oturumunun kendi izole ad alanı olacaktır</string>
<string name="android_o_not_support">Android 8.0 ve üzerinde desteklenmiyor</string>
<string name="disable_fingerprint">Parmak izi ayarlanmadı veya cihaz desteği yok</string> <string name="disable_fingerprint">Parmak izi ayarlanmadı veya cihaz desteği yok</string>
<string name="settings_download_path_error">Klasör oluşturma hatası. Depolama kök dizininden erişilebilir olmalı ve bir dosya olmamalıdır.</string> <string name="settings_download_path_error">Klasör oluşturma hatası. Depolama kök dizininden erişilebilir olmalı ve bir dosya olmamalıdır.</string>

View File

@ -88,7 +88,6 @@
<string name="reboot_delay_toast">Перезавантаження через 5 секунд…</string> <string name="reboot_delay_toast">Перезавантаження через 5 секунд…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Закрити</string>
<string name="repo_install_title">Встановити %1$s</string> <string name="repo_install_title">Встановити %1$s</string>
<string name="repo_install_msg">Бажаєте встановити %1$s ?</string> <string name="repo_install_msg">Бажаєте встановити %1$s ?</string>
<string name="download">Завантажити</string> <string name="download">Завантажити</string>
@ -106,8 +105,8 @@
<string name="hide_manager_fail_toast">Не вдалося приховати Magisk Manager.</string> <string name="hide_manager_fail_toast">Не вдалося приховати Magisk Manager.</string>
<string name="open_link_failed_toast">Не знайдено програм для відкриття посилання.</string> <string name="open_link_failed_toast">Не знайдено програм для відкриття посилання.</string>
<string name="warning">Попередження</string> <string name="warning">Попередження</string>
<string name="complete_uninstall">Видалення виконано</string> <string name="complete_uninstall">Повне видалення</string>
<string name="restore_img">Відновити образи</string> <string name="restore_img">Відновити розділи</string>
<string name="restore_img_msg">Відновлення…</string> <string name="restore_img_msg">Відновлення…</string>
<string name="restore_done">Відновлення завершено!</string> <string name="restore_done">Відновлення завершено!</string>
<string name="restore_fail">Немає резервної копії оригінального boot образу</string> <string name="restore_fail">Немає резервної копії оригінального boot образу</string>
@ -140,13 +139,17 @@
<string name="settings_update_beta">Бета реліз</string> <string name="settings_update_beta">Бета реліз</string>
<string name="settings_update_custom">Власний</string> <string name="settings_update_custom">Власний</string>
<string name="settings_update_custom_msg">Вставте власний URL</string> <string name="settings_update_custom_msg">Вставте власний URL</string>
<string name="settings_core_only_title">Режим ядра Magisk</string> <string name="settings_core_only_title">Режим Magisk Core</string>
<string name="settings_core_only_summary">Увімкнути тільки можливості ядра. MagiskSU i Magisk Hide залишуться увімкненими, проте ніякі модулі не будуть завантажені.</string> <string name="settings_core_only_summary">Увімкнути тільки можливості ядра. MagiskSU i Magisk Hide залишуться увімкненими, проте ніякі модулі не будуть завантажені.</string>
<string name="settings_magiskhide_summary">Приховати Magisk від різних форм виявлень.</string> <string name="settings_magiskhide_summary">Приховати Magisk від різних форм виявлень.</string>
<string name="settings_hosts_title">Позасистемні хости</string> <string name="settings_hosts_title">Позасистемні хости</string>
<string name="settings_hosts_summary">Підтримка позасистемних хостів для програм блокування реклами.</string> <string name="settings_hosts_summary">Підтримка позасистемних хостів для програм блокування реклами.</string>
<string name="settings_hosts_toast">Додано модуль позасистемних хостів</string> <string name="settings_hosts_toast">Додано модуль позасистемних хостів</string>
<string name="settings_app_name">Введіть бажане ім\'я застосунку</string>
<string name="settings_app_name_hint">Нове ім\'я</string>
<string name="settings_app_name_helper">Застосунок буде перезібрано з цим ім\'ям</string>
<string name="settings_app_name_error">Неправильний формат</string>
<string name="settings_su_app_adb">Програми і ADB</string> <string name="settings_su_app_adb">Програми і ADB</string>
<string name="settings_su_app">Програми</string> <string name="settings_su_app">Програми</string>
<string name="settings_su_adb">ADB</string> <string name="settings_su_adb">ADB</string>
@ -183,7 +186,6 @@
<string name="global_summary">Всі сеанси Суперкористувача використовують глобальний простір імен.</string> <string name="global_summary">Всі сеанси Суперкористувача використовують глобальний простір імен.</string>
<string name="requester_summary">Сеанси Суперкористувача наслідують простір імен запитувача.</string> <string name="requester_summary">Сеанси Суперкористувача наслідують простір імен запитувача.</string>
<string name="isolate_summary">Кожнен сеанс Суперкористувача має власний ізольований простір імен.</string> <string name="isolate_summary">Кожнен сеанс Суперкористувача має власний ізольований простір імен.</string>
<string name="android_o_not_support">Не працює на Android 8.0+.</string>
<string name="disable_fingerprint">Немає відбитків пальця або пристрій не підтримується.</string> <string name="disable_fingerprint">Немає відбитків пальця або пристрій не підтримується.</string>
<string name="settings_download_path_error">Помилка створення папки. Вона повинна бути доступна з кореневої директорії сховища і не повинна бути файлом.</string> <string name="settings_download_path_error">Помилка створення папки. Вона повинна бути доступна з кореневої директорії сховища і не повинна бути файлом.</string>

View File

@ -61,7 +61,6 @@
<string name="app_changelog">Nhật ký thay đổi của ứng dụng</string> <string name="app_changelog">Nhật ký thay đổi của ứng dụng</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Đóng</string>
<string name="repo_install_title">Cài đặt %1$s</string> <string name="repo_install_title">Cài đặt %1$s</string>
<string name="repo_install_msg">Bạn muốn cài đặt %1$s ?</string> <string name="repo_install_msg">Bạn muốn cài đặt %1$s ?</string>
<string name="download">Tải xuống</string> <string name="download">Tải xuống</string>
@ -159,7 +158,6 @@
<string name="global_summary">Tất cả các phiên root sử dụng không gian tên gắn kết chung.</string> <string name="global_summary">Tất cả các phiên root sử dụng không gian tên gắn kết chung.</string>
<string name="requester_summary">Các phiên root sẽ kế thừa không gian tên của người yêu cầu.</string> <string name="requester_summary">Các phiên root sẽ kế thừa không gian tên của người yêu cầu.</string>
<string name="isolate_summary">Mỗi phiên root sẽ có không gian tên riêng biệt.</string> <string name="isolate_summary">Mỗi phiên root sẽ có không gian tên riêng biệt.</string>
<string name="android_o_not_support">Không hỗ trợ Android 8.0+.</string>
<string name="disable_fingerprint">Không có dấu vân tay nào được thiết lập hoặc thiết bị không hỗ trợ.</string> <string name="disable_fingerprint">Không có dấu vân tay nào được thiết lập hoặc thiết bị không hỗ trợ.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -88,12 +88,11 @@
<string name="reboot_delay_toast">5 秒后重启</string> <string name="reboot_delay_toast">5 秒后重启</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">关闭</string>
<string name="repo_install_title">安装 %1$s</string> <string name="repo_install_title">安装 %1$s</string>
<string name="repo_install_msg">是否安装 %1$s </string> <string name="repo_install_msg">是否安装 %1$s </string>
<string name="download">下载</string> <string name="download">下载</string>
<string name="reboot">重启</string> <string name="reboot">重启</string>
<string name="settings_reboot_toast">重启以应用设置</string> <string name="settings_reboot_toast">重启设备以应用设置</string>
<string name="release_notes">发布说明</string> <string name="release_notes">发布说明</string>
<string name="repo_cache_cleared">仓库缓存已清除</string> <string name="repo_cache_cleared">仓库缓存已清除</string>
@ -147,6 +146,10 @@
<string name="settings_hosts_summary">为广告屏蔽应用提供 Systemless hosts 支持</string> <string name="settings_hosts_summary">为广告屏蔽应用提供 Systemless hosts 支持</string>
<string name="settings_hosts_toast">已添加 systemless hosts 模块</string> <string name="settings_hosts_toast">已添加 systemless hosts 模块</string>
<string name="settings_app_name">输入想要的应用名称</string>
<string name="settings_app_name_hint">新名称</string>
<string name="settings_app_name_helper">新应用将使用此名称</string>
<string name="settings_app_name_error">输入无效</string>
<string name="settings_su_app_adb">应用和 ADB</string> <string name="settings_su_app_adb">应用和 ADB</string>
<string name="settings_su_app">仅应用</string> <string name="settings_su_app">仅应用</string>
<string name="settings_su_adb">仅 ADB</string> <string name="settings_su_adb">仅 ADB</string>
@ -183,7 +186,6 @@
<string name="global_summary">所有的 ROOT 会话使用全局挂载命名空间</string> <string name="global_summary">所有的 ROOT 会话使用全局挂载命名空间</string>
<string name="requester_summary">ROOT 会话继承原程序的命名空间</string> <string name="requester_summary">ROOT 会话继承原程序的命名空间</string>
<string name="isolate_summary">每一个 ROOT 会话使用自己独立的命名空间</string> <string name="isolate_summary">每一个 ROOT 会话使用自己独立的命名空间</string>
<string name="android_o_not_support">不支持 Android 8.0+</string>
<string name="disable_fingerprint">没有设置指纹或设备不支持</string> <string name="disable_fingerprint">没有设置指纹或设备不支持</string>
<string name="settings_download_path_error">创建文件夹出错。路径需要位于内部存储空间,并且不能有同名文件。</string> <string name="settings_download_path_error">创建文件夹出错。路径需要位于内部存储空间,并且不能有同名文件。</string>

View File

@ -84,7 +84,6 @@
<string name="reboot_delay_toast">將在 5 秒後重新啟動…</string> <string name="reboot_delay_toast">將在 5 秒後重新啟動…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">關閉</string>
<string name="repo_install_title">安裝 %1$s</string> <string name="repo_install_title">安裝 %1$s</string>
<string name="repo_install_msg">即將安裝 %1$s</string> <string name="repo_install_msg">即將安裝 %1$s</string>
<string name="download">下載</string> <string name="download">下載</string>
@ -177,7 +176,6 @@
<string name="global_summary">所有 Root 工作階段皆使用全域 Namespace</string> <string name="global_summary">所有 Root 工作階段皆使用全域 Namespace</string>
<string name="requester_summary">所有 Root 工作階段皆繼承原程式 Namespace</string> <string name="requester_summary">所有 Root 工作階段皆繼承原程式 Namespace</string>
<string name="isolate_summary">所有 Root 工作階段個別擁有獨立 Namespace</string> <string name="isolate_summary">所有 Root 工作階段個別擁有獨立 Namespace</string>
<string name="android_o_not_support">不支援 Android 8.0 及更新版本</string>
<string name="disable_fingerprint">未設定指紋或無指紋辨識器</string> <string name="disable_fingerprint">未設定指紋或無指紋辨識器</string>
<!--Superuser--> <!--Superuser-->

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Static strings -->
<string name="app_name" translatable="false">Magisk Manager</string>
<string name="re_app_name" translatable="false">Manager</string>
<string name="magisk" translatable="false">Magisk</string>
<string name="magiskhide" translatable="false">Magisk Hide</string>
<string name="empty" translatable="false"/>
</resources>

View File

@ -18,7 +18,7 @@
<string name="checking_safetyNet_status">Checking SafetyNet status…</string> <string name="checking_safetyNet_status">Checking SafetyNet status…</string>
<string name="safetyNet_check_success">SafetyNet Check Success</string> <string name="safetyNet_check_success">SafetyNet Check Success</string>
<string name="safetyNet_api_error">SafetyNet API Error</string> <string name="safetyNet_api_error">SafetyNet API Error</string>
<string name="safetyNet_res_invalid">The response is invalid.</string> <string name="safetyNet_res_invalid">The response is invalid</string>
<string name="magisk_up_to_date">Magisk is up to date</string> <string name="magisk_up_to_date">Magisk is up to date</string>
<string name="manager_up_to_date">Magisk Manager is up to date</string> <string name="manager_up_to_date">Magisk Manager is up to date</string>
<string name="advanced_settings_title">Advanced Settings</string> <string name="advanced_settings_title">Advanced Settings</string>
@ -29,7 +29,7 @@
<string name="latest_version">Latest: %1$s</string> <string name="latest_version">Latest: %1$s</string>
<string name="uninstall">Uninstall</string> <string name="uninstall">Uninstall</string>
<string name="uninstall_magisk_title">Uninstall Magisk</string> <string name="uninstall_magisk_title">Uninstall Magisk</string>
<string name="uninstall_magisk_msg">All modules will be disabled/removed. Root will be removed, and your data potentially encrypted if not already.</string> <string name="uninstall_magisk_msg">All modules will be disabled/removed!\nRoot will be removed!\nYour data potentially encrypted if not already!</string>
<string name="update">Update</string> <string name="update">Update</string>
<string name="core_only_enabled">(Core only mode enabled)</string> <string name="core_only_enabled">(Core only mode enabled)</string>
@ -76,7 +76,7 @@
<string name="manager_update_title">Magisk Manager Update Available!</string> <string name="manager_update_title">Magisk Manager Update Available!</string>
<!-- Installation --> <!-- Installation -->
<string name="manager_download_install">Press to download and install.</string> <string name="manager_download_install">Press to download and install</string>
<string name="download_zip_only">Download Zip Only</string> <string name="download_zip_only">Download Zip Only</string>
<string name="direct_install">Direct Install (Recommended)</string> <string name="direct_install">Direct Install (Recommended)</string>
<string name="install_inactive_slot">Install to Inactive Slot (After OTA)</string> <string name="install_inactive_slot">Install to Inactive Slot (After OTA)</string>
@ -88,12 +88,11 @@
<string name="reboot_delay_toast">Rebooting in 5 seconds…</string> <string name="reboot_delay_toast">Rebooting in 5 seconds…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="close">Close</string>
<string name="repo_install_title">Install %1$s</string> <string name="repo_install_title">Install %1$s</string>
<string name="repo_install_msg">Do you want to install %1$s now?</string> <string name="repo_install_msg">Do you want to install %1$s now?</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="reboot">Reboot</string> <string name="reboot">Reboot</string>
<string name="settings_reboot_toast">Reboot to apply settings.</string> <string name="settings_reboot_toast">Reboot to apply settings</string>
<string name="release_notes">Release notes</string> <string name="release_notes">Release notes</string>
<string name="repo_cache_cleared">Repo cache cleared</string> <string name="repo_cache_cleared">Repo cache cleared</string>
@ -104,7 +103,7 @@
<string name="failure">Failed</string> <string name="failure">Failed</string>
<string name="hide_manager_title">Hiding Magisk Manager…</string> <string name="hide_manager_title">Hiding Magisk Manager…</string>
<string name="hide_manager_fail_toast">Hide Magisk Manager failed.</string> <string name="hide_manager_fail_toast">Hide Magisk Manager failed.</string>
<string name="open_link_failed_toast">No application found to open the link.</string> <string name="open_link_failed_toast">No application found to open the link</string>
<string name="warning">Warning</string> <string name="warning">Warning</string>
<string name="complete_uninstall">Complete Uninstall</string> <string name="complete_uninstall">Complete Uninstall</string>
<string name="restore_img">Restore Images</string> <string name="restore_img">Restore Images</string>
@ -113,7 +112,7 @@
<string name="restore_fail">Stock backup does not exist!</string> <string name="restore_fail">Stock backup does not exist!</string>
<string name="proprietary_title">Download Proprietary Code</string> <string name="proprietary_title">Download Proprietary Code</string>
<string name="proprietary_notice">Magisk Manager is FOSS and doesn\'t contain Google\'s proprietary SafetyNet API code.\n\nWill you allow Magisk Manager to download an extension (contains GoogleApiClient) for SafetyNet checks?</string> <string name="proprietary_notice">Magisk Manager is FOSS and doesn\'t contain Google\'s proprietary SafetyNet API code.\n\nWill you allow Magisk Manager to download an extension (contains GoogleApiClient) for SafetyNet checks?</string>
<string name="setup_fail">Setup failed.</string> <string name="setup_fail">Setup failed</string>
<string name="env_fix_title">Requires Additional Setup</string> <string name="env_fix_title">Requires Additional Setup</string>
<string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now?</string> <string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now?</string>
<string name="setup_msg">Running environment setup…</string> <string name="setup_msg">Running environment setup…</string>
@ -121,32 +120,36 @@
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">General</string> <string name="settings_general_category">General</string>
<string name="settings_dark_theme_title">Dark Theme</string> <string name="settings_dark_theme_title">Dark Theme</string>
<string name="settings_dark_theme_summary">Enable dark theme.</string> <string name="settings_dark_theme_summary">Enable dark theme</string>
<string name="settings_download_path_title">Download path</string> <string name="settings_download_path_title">Download path</string>
<string name="settings_download_path_message">Files will be saved to %1$s</string> <string name="settings_download_path_message">Files will be saved to %1$s</string>
<string name="settings_clear_cache_title">Clear Repo Cache</string> <string name="settings_clear_cache_title">Clear Repo Cache</string>
<string name="settings_clear_cache_summary">Clear the cached information for online repos. This forces the app to refresh online.</string> <string name="settings_clear_cache_summary">Clear the cached information for online repos. This forces the app to refresh online</string>
<string name="settings_hide_manager_title">Hide Magisk Manager</string> <string name="settings_hide_manager_title">Hide Magisk Manager</string>
<string name="settings_hide_manager_summary">Repackage Magisk Manager with random package name.</string> <string name="settings_hide_manager_summary">Repackage Magisk Manager with random package and app names</string>
<string name="settings_restore_manager_title">Restore Magisk Manager</string> <string name="settings_restore_manager_title">Restore Magisk Manager</string>
<string name="settings_restore_manager_summary">Restore Magisk Manager with original package</string> <string name="settings_restore_manager_summary">Restore Magisk Manager with original package and app names</string>
<string name="language">Language</string> <string name="language">Language</string>
<string name="system_default">(System Default)</string> <string name="system_default">(System Default)</string>
<string name="settings_update">Update Settings</string> <string name="settings_update">Update Settings</string>
<string name="settings_check_update_title">Check Updates</string> <string name="settings_check_update_title">Check Updates</string>
<string name="settings_check_update_summary">Periodically check for updates in the background.</string> <string name="settings_check_update_summary">Periodically check for updates in the background</string>
<string name="settings_update_channel_title">Update Channel</string> <string name="settings_update_channel_title">Update Channel</string>
<string name="settings_update_stable">Stable</string> <string name="settings_update_stable">Stable</string>
<string name="settings_update_beta">Beta</string> <string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Custom</string> <string name="settings_update_custom">Custom</string>
<string name="settings_update_custom_msg">Insert a custom URL</string> <string name="settings_update_custom_msg">Insert a custom URL</string>
<string name="settings_core_only_title">Magisk Core Only Mode</string> <string name="settings_core_only_title">Magisk Core Only Mode</string>
<string name="settings_core_only_summary">Enable only core features. MagiskSU and MagiskHide will still be enabled, but no modules will be loaded.</string> <string name="settings_core_only_summary">Enable only core features. MagiskSU and MagiskHide will still be enabled, but no modules will be loaded</string>
<string name="settings_magiskhide_summary">Hide Magisk from various forms of detection.</string> <string name="settings_magiskhide_summary">Hide Magisk from various forms of detection</string>
<string name="settings_hosts_title">Systemless hosts</string> <string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_summary">Systemless hosts support for Adblock apps.</string> <string name="settings_hosts_summary">Systemless hosts support for Adblock apps</string>
<string name="settings_hosts_toast">Added systemless hosts module</string> <string name="settings_hosts_toast">Added systemless hosts module</string>
<string name="settings_app_name">Type desired app name</string>
<string name="settings_app_name_hint">New name</string>
<string name="settings_app_name_helper">App will be repackaged to this name</string>
<string name="settings_app_name_error">Invalid format</string>
<string name="settings_su_app_adb">Apps and ADB</string> <string name="settings_su_app_adb">Apps and ADB</string>
<string name="settings_su_app">Apps only</string> <string name="settings_su_app">Apps only</string>
<string name="settings_su_adb">ADB only</string> <string name="settings_su_adb">ADB only</string>
@ -172,19 +175,18 @@
<string name="settings_owner_only">Device Owner Only</string> <string name="settings_owner_only">Device Owner Only</string>
<string name="settings_owner_manage">Device Owner Managed</string> <string name="settings_owner_manage">Device Owner Managed</string>
<string name="settings_user_independent">User-Independent</string> <string name="settings_user_independent">User-Independent</string>
<string name="owner_only_summary">Only owner has root access.</string> <string name="owner_only_summary">Only owner has root access</string>
<string name="owner_manage_summary">Only owner can manage root access and receive request prompts.</string> <string name="owner_manage_summary">Only owner can manage root access and receive request prompts</string>
<string name="user_indepenent_summary">Each user has his/her own separate root rules.</string> <string name="user_indepenent_summary">Each user has his/her own separate root rules</string>
<string name="mount_namespace_mode">Mount Namespace Mode</string> <string name="mount_namespace_mode">Mount Namespace Mode</string>
<string name="settings_ns_global">Global Namespace</string> <string name="settings_ns_global">Global Namespace</string>
<string name="settings_ns_requester">Inherit Namespace</string> <string name="settings_ns_requester">Inherit Namespace</string>
<string name="settings_ns_isolate">Isolated Namespace</string> <string name="settings_ns_isolate">Isolated Namespace</string>
<string name="global_summary">All root sessions use the global mount namespace.</string> <string name="global_summary">All root sessions use the global mount namespace</string>
<string name="requester_summary">Root sessions will inherit their requester\'s namespace.</string> <string name="requester_summary">Root sessions will inherit their requester\'s namespace</string>
<string name="isolate_summary">Each root session will have its own isolated namespace.</string> <string name="isolate_summary">Each root session will have its own isolated namespace</string>
<string name="android_o_not_support">Does not support Android 8.0+.</string> <string name="disable_fingerprint">No fingerprints were set or no device support</string>
<string name="disable_fingerprint">No fingerprints were set or no device support.</string>
<string name="settings_download_path_error">Error creating folder. It must be accessible from storage root directory and must not be a file.</string> <string name="settings_download_path_error">Error creating folder. It must be accessible from storage root directory and must not be a file.</string>
<!--Superuser--> <!--Superuser-->

View File

@ -62,7 +62,7 @@ subprojects {
android { android {
signingConfigs { signingConfigs {
config { config {
storeFile rootProject.file('release-key.jks') storeFile new File(props['keyStore'])
storePassword props['keyStorePass'] storePassword props['keyStorePass']
keyAlias props['keyAlias'] keyAlias props['keyAlias']
keyPassword props['keyPass'] keyPassword props['keyPass']
@ -85,13 +85,12 @@ subprojects {
} }
aaptOptions { aaptOptions {
// Preserve stub resource IDs // Handle resource IDs
File publicTxt = rootProject.file('stub-public.txt') File resId = project.file('res-ids.txt')
if (publicTxt.exists()) { if (resId.exists())
additionalParameters "--stable-ids", "${publicTxt.absolutePath}" additionalParameters "--stable-ids", "${resId.absolutePath}"
} else if (module.name == 'stub') { else
additionalParameters "--emit-ids", "${publicTxt.absolutePath}" additionalParameters "--emit-ids", "${resId.absolutePath}"
}
} }
} }
} }

154
build.py
View File

@ -3,7 +3,8 @@ import sys
import os import os
import subprocess import subprocess
if os.name == 'nt': is_windows = os.name == 'nt'
if is_windows:
import colorama import colorama
colorama.init() colorama.init()
@ -44,6 +45,7 @@ import shutil
import lzma import lzma
import tempfile import tempfile
# Constants
if 'ANDROID_NDK_HOME' in os.environ: if 'ANDROID_NDK_HOME' in os.environ:
ndk_build = os.path.join(os.environ['ANDROID_NDK_HOME'], 'ndk-build') ndk_build = os.path.join(os.environ['ANDROID_NDK_HOME'], 'ndk-build')
else: else:
@ -51,14 +53,16 @@ else:
os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
cpu_count = multiprocessing.cpu_count() cpu_count = multiprocessing.cpu_count()
gradlew = os.path.join('.', 'gradlew' + ('.bat' if os.name == 'nt' else '')) gradlew = os.path.join('.', 'gradlew' + ('.bat' if is_windows else ''))
archs = ['armeabi-v7a', 'x86'] archs = ['armeabi-v7a', 'x86']
arch64 = ['arm64-v8a', 'x86_64'] arch64 = ['arm64-v8a', 'x86_64']
keystore = 'release-key.jks'
config = {}
support_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy', 'busybox', 'test'] support_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy', 'busybox', 'test']
default_targets = ['magisk', 'magiskinit', 'magiskboot', 'busybox'] default_targets = ['magisk', 'magiskinit', 'magiskboot', 'busybox']
build_tools = os.path.join(os.environ['ANDROID_HOME'], 'build-tools', '29.0.2')
# Global vars
config = {}
STDOUT = None
def mv(source, target): def mv(source, target):
try: try:
@ -96,18 +100,56 @@ def mkdir_p(path, mode=0o777):
os.makedirs(path, mode, exist_ok=True) os.makedirs(path, mode, exist_ok=True)
def execv(cmd, redirect=None): def execv(cmd):
return subprocess.run(cmd, stdout=redirect if redirect != None else STDOUT) return subprocess.run(cmd, stdout=STDOUT)
def system(cmd, redirect=None): def system(cmd):
return subprocess.run(cmd, shell=True, stdout=redirect if redirect != None else STDOUT) return subprocess.run(cmd, shell=True, stdout=STDOUT)
def xz(data): def xz(data):
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE) return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
def load_config(args):
# Some default values
config['outdir'] = 'out'
config['prettyName'] = 'false'
config['keyStore'] = 'release-key.jks'
# Load prop file
if not os.path.exists(args.config):
error(f'Please make sure {args.config} existed')
with open(args.config, 'r') as f:
for line in [l.strip(' \t\r\n') for l in f]:
if line.startswith('#') or len(line) == 0:
continue
prop = line.split('=')
if len(prop) != 2:
continue
config[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
config['prettyName'] = config['prettyName'].lower() == 'true'
# Sanitize configs
if 'version' not in config or 'versionCode' not in config:
error('Config error: "version" and "versionCode" is required')
try:
config['versionCode'] = int(config['versionCode'])
except ValueError:
error('Config error: "versionCode" is required to be an integer')
if args.release and not os.path.exists(config['keyStore']):
error(f'Config error: assign "keyStore" to a java keystore')
mkdir_p(config['outdir'])
global STDOUT
STDOUT = None if args.verbose else subprocess.DEVNULL
def zip_with_msg(zip_file, source, target): def zip_with_msg(zip_file, source, target):
if not os.path.exists(source): if not os.path.exists(source):
error(f'{source} does not exist! Try build \'binary\' and \'apk\' before zipping!') error(f'{source} does not exist! Try build \'binary\' and \'apk\' before zipping!')
@ -125,12 +167,13 @@ def collect_binary():
def clean_elf(): def clean_elf():
if os.name == 'nt': if is_windows:
elf_cleaner = os.path.join('tools', 'elf-cleaner.exe') elf_cleaner = os.path.join('tools', 'elf-cleaner.exe')
else: else:
elf_cleaner = os.path.join('native', 'out', 'elf-cleaner') elf_cleaner = os.path.join('native', 'out', 'elf-cleaner')
if not os.path.exists(elf_cleaner): if not os.path.exists(elf_cleaner):
execv(['g++', 'tools/termux-elf-cleaner/termux-elf-cleaner.cpp', '-o', elf_cleaner]) execv(['g++', 'tools/termux-elf-cleaner/termux-elf-cleaner.cpp',
'-o', elf_cleaner])
args = [elf_cleaner] args = [elf_cleaner]
args.extend(os.path.join('native', 'out', arch, 'magisk') for arch in archs + arch64) args.extend(os.path.join('native', 'out', arch, 'magisk') for arch in archs + arch64)
execv(args) execv(args)
@ -152,7 +195,7 @@ def sign_zip(unsigned, output, release):
header('* Signing Zip') header('* Signing Zip')
proc = execv(['java', '-jar', zipsigner, keystore, config['keyStorePass'], proc = execv(['java', '-jar', zipsigner, config['keyStore'], config['keyStorePass'],
config['keyAlias'], config['keyPass'], unsigned, output]) config['keyAlias'], config['keyPass'], unsigned, output])
if proc.returncode != 0: if proc.returncode != 0:
@ -259,14 +302,47 @@ def build_apk(args, module):
proc = execv([gradlew, f'{module}:assemble{build_type}', proc = execv([gradlew, f'{module}:assemble{build_type}',
'-PconfigPath=' + os.path.abspath(args.config)]) '-PconfigPath=' + os.path.abspath(args.config)])
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk Manager failed!') error(f'Build {module} failed!')
build_type = build_type.lower() build_type = build_type.lower()
apk = f'{module}-{build_type}.apk' apk = f'{module}-{build_type}.apk'
source = os.path.join(module, 'build', 'outputs', 'apk', build_type, apk) source = os.path.join(module, 'build', 'outputs', 'apk', build_type, apk)
target = os.path.join(config['outdir'], apk) target = os.path.join(config['outdir'], apk)
if args.release:
zipalign = os.path.join(build_tools, 'zipalign' + ('.exe' if is_windows else ''))
aapt2 = os.path.join(build_tools, 'aapt2' + ('.exe' if is_windows else ''))
apksigner = os.path.join(build_tools, 'apksigner' + ('.bat' if is_windows else ''))
try:
with tempfile.NamedTemporaryFile(delete=False) as f:
tmp = f.name
# AAPT2 optimization
execv([aapt2, 'optimize', '-o', tmp, '--enable-resource-obfuscation',
'--enable-resource-path-shortening', source])
# Recompress everything just to piss people off
with zipfile.ZipFile(source, 'w', compression=zipfile.ZIP_DEFLATED) as zout:
with zipfile.ZipFile(tmp) as zin:
for e in zin.namelist():
zout.writestr(e, zin.read(e))
# Zipalign
execv([zipalign, '-fz', '4', source, target])
# Sign APK
execv([apksigner, 'sign', '--v1-signer-name', 'CERT',
'--ks', config['keyStore'],
'--ks-pass', f'pass:{config["keyStorePass"]}',
'--ks-key-alias', config['keyAlias'],
'--key-pass', f'pass:{config["keyPass"]}', target])
finally:
rm(tmp)
rm(source)
else:
mv(source, target) mv(source, target)
header('Output: ' + target) header('Output: ' + target)
return target return target
@ -309,7 +385,8 @@ def build_snet(args):
def zip_main(args): def zip_main(args):
header('* Packing Flashable Zip') header('* Packing Flashable Zip')
unsigned = tempfile.mkstemp()[1] with tempfile.NamedTemporaryFile(delete=False) as f:
unsigned = f.name
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf: with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# update-binary # update-binary
@ -367,13 +444,15 @@ def zip_main(args):
output = os.path.join(config['outdir'], f'Magisk-v{config["version"]}.zip' if config['prettyName'] else output = os.path.join(config['outdir'], f'Magisk-v{config["version"]}.zip' if config['prettyName'] else
'magisk-release.zip' if args.release else 'magisk-debug.zip') 'magisk-release.zip' if args.release else 'magisk-debug.zip')
sign_zip(unsigned, output, args.release) sign_zip(unsigned, output, args.release)
rm(unsigned)
header('Output: ' + output) header('Output: ' + output)
def zip_uninstaller(args): def zip_uninstaller(args):
header('* Packing Uninstaller Zip') header('* Packing Uninstaller Zip')
unsigned = tempfile.mkstemp()[1] with tempfile.NamedTemporaryFile(delete=False) as f:
unsigned = f.name
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf: with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# update-binary # update-binary
@ -412,6 +491,7 @@ def zip_uninstaller(args):
output = os.path.join(config['outdir'], f'Magisk-uninstaller-{datestr}.zip' output = os.path.join(config['outdir'], f'Magisk-uninstaller-{datestr}.zip'
if config['prettyName'] else 'magisk-uninstaller.zip') if config['prettyName'] else 'magisk-uninstaller.zip')
sign_zip(unsigned, output, args.release) sign_zip(unsigned, output, args.release)
rm(unsigned)
header('Output: ' + output) header('Output: ' + output)
@ -444,28 +524,28 @@ def build_all(args):
parser = argparse.ArgumentParser(description='Magisk build script') parser = argparse.ArgumentParser(description='Magisk build script')
parser.add_argument('-r', '--release', action='store_true', parser.add_argument('-r', '--release', action='store_true',
help='compile Magisk for release') help='compile in release mode')
parser.add_argument('-v', '--verbose', action='store_true', parser.add_argument('-v', '--verbose', action='store_true',
help='verbose output') help='verbose output')
parser.add_argument('-c', '--config', default='config.prop', parser.add_argument('-c', '--config', default='config.prop',
help='config file location') help='override config file (default: config.prop)')
subparsers = parser.add_subparsers(title='actions') subparsers = parser.add_subparsers(title='actions')
all_parser = subparsers.add_parser( all_parser = subparsers.add_parser(
'all', help='build everything (binaries/apks/zips)') 'all', help='build binaries, apks, zips')
all_parser.set_defaults(func=build_all) all_parser.set_defaults(func=build_all)
binary_parser = subparsers.add_parser('binary', help='build binaries') binary_parser = subparsers.add_parser('binary', help='build binaries')
binary_parser.add_argument( binary_parser.add_argument(
'target', nargs='*', help=f"Either {', '.join(support_targets)}, \ 'target', nargs='*', help=f"{', '.join(support_targets)}, \
or empty for defaults ({', '.join(default_targets)})") or empty for defaults ({', '.join(default_targets)})")
binary_parser.set_defaults(func=build_binary) binary_parser.set_defaults(func=build_binary)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK') app_parser = subparsers.add_parser('app', help='build Magisk Manager')
apk_parser.set_defaults(func=build_app) app_parser.set_defaults(func=build_app)
stub_parser = subparsers.add_parser( stub_parser = subparsers.add_parser(
'stub', help='build stub Magisk Manager APK') 'stub', help='build stub Magisk Manager')
stub_parser.set_defaults(func=build_stub) stub_parser.set_defaults(func=build_stub)
snet_parser = subparsers.add_parser( snet_parser = subparsers.add_parser(
@ -480,9 +560,9 @@ un_parser = subparsers.add_parser(
'uninstaller', help='create flashable uninstaller') 'uninstaller', help='create flashable uninstaller')
un_parser.set_defaults(func=zip_uninstaller) un_parser.set_defaults(func=zip_uninstaller)
clean_parser = subparsers.add_parser('clean', help='cleanup.') clean_parser = subparsers.add_parser('clean', help='cleanup')
clean_parser.add_argument( clean_parser.add_argument(
'target', nargs='*', help='Either native, java, or empty to clean both.') 'target', nargs='*', help='native, java, or empty to clean both')
clean_parser.set_defaults(func=cleanup) clean_parser.set_defaults(func=cleanup)
if len(sys.argv) == 1: if len(sys.argv) == 1:
@ -490,33 +570,7 @@ if len(sys.argv) == 1:
sys.exit(1) sys.exit(1)
args = parser.parse_args() args = parser.parse_args()
load_config(args)
# Some default values
config['outdir'] = 'out'
config['prettyName'] = 'false'
with open(args.config, 'r') as f:
for line in [l.strip(' \t\r\n') for l in f]:
if line.startswith('#') or len(line) == 0:
continue
prop = line.split('=')
config[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
if 'version' not in config or 'versionCode' not in config:
error('"version" and "versionCode" is required in "config.prop"')
try:
config['versionCode'] = int(config['versionCode'])
except ValueError:
error('"versionCode" is required to be an integer')
config['prettyName'] = config['prettyName'].lower() == 'true'
mkdir_p(config['outdir'])
if args.release and not os.path.exists(keystore):
error(f'Please generate a java keystore and place it in "{keystore}"')
STDOUT = None if args.verbose else subprocess.DEVNULL
# Call corresponding functions # Call corresponding functions
args.func(args) args.func(args)

View File

@ -13,8 +13,9 @@ outdir=out
prettyName=false prettyName=false
# Only used when building with release flag # Only used when building with release flag
# These passwords are used along with release-key.jks to sign APKs and zips # These passwords are used along with keyStore to sign APKs and zips
# keyPass is the pwd for the specified keyAlias # keyPass is the pwd for the specified keyAlias
keyStore=release-key.jks
keyStorePass= keyStorePass=
keyAlias= keyAlias=
keyPass= keyPass=

View File

@ -1,4 +1,4 @@
# Magisk Documentations # Magisk Documentation
(Updated on 2019.9.19) (Updated on 2019.9.19)
- [Installation](install.md) - [Installation](install.md)
@ -6,7 +6,7 @@
- [OTA Installation](tutorials.md#ota-installation) - [OTA Installation](tutorials.md#ota-installation)
- [Best Practices for MagiskHide](tutorials.md#best-practices-for-magiskhide) - [Best Practices for MagiskHide](tutorials.md#best-practices-for-magiskhide)
The followings are for developers The following sections are for developers
- [Magisk Details](details.md) - [Magisk Details](details.md)
- [Magisk Tools](tools.md) - [Magisk Tools](tools.md)

View File

@ -54,6 +54,7 @@ static void *request_handler(void *args) {
case BOOT_COMPLETE: case BOOT_COMPLETE:
case SQLITE_CMD: case SQLITE_CMD:
case BROADCAST_ACK: case BROADCAST_ACK:
case BROADCAST_TEST:
if (credential.uid != 0) { if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED); write_int(client, ROOT_REQUIRED);
close(client); close(client);
@ -91,9 +92,10 @@ static void *request_handler(void *args) {
exec_sql(client); exec_sql(client);
break; break;
case BROADCAST_ACK: case BROADCAST_ACK:
LOGD("* Use broadcasts for su logging and notify\n"); broadcast_ack(client);
CONNECT_BROADCAST = true; break;
close(client); case BROADCAST_TEST:
broadcast_test(client);
break; break;
case REMOVE_MODULES: case REMOVE_MODULES:
if (credential.uid == UID_SHELL || credential.uid == UID_ROOT) { if (credential.uid == UID_SHELL || credential.uid == UID_ROOT) {

View File

@ -219,7 +219,6 @@ int get_db_strings(db_strings &str, int key) {
char *err; char *err;
auto string_cb = [&](db_row &row) -> bool { auto string_cb = [&](db_row &row) -> bool {
str[row["key"]] = row["value"]; str[row["key"]] = row["value"];
LOGD("magiskdb: query %s=[%s]\n", row["key"].data(), row["value"].data());
return true; return true;
}; };
if (key >= 0) { if (key >= 0) {
@ -273,6 +272,7 @@ int validate_manager(string &alt_pkg, int userid, struct stat *st) {
} }
void exec_sql(int client) { void exec_sql(int client) {
run_finally f([=]{ close(client); });
char *sql = read_string(client); char *sql = read_string(client);
char *err = db_exec(sql, [&](db_row &row) -> bool { char *err = db_exec(sql, [&](db_row &row) -> bool {
string out; string out;
@ -289,9 +289,6 @@ void exec_sql(int client) {
return true; return true;
}); });
free(sql); free(sql);
db_err_cmd(err,
write_int(client, 0); write_int(client, 0);
return; db_err_cmd(err, return; );
);
close(client);
} }

View File

@ -9,7 +9,6 @@
#include <magisk.h> #include <magisk.h>
#include <daemon.h> #include <daemon.h>
#include <selinux.h> #include <selinux.h>
#include <db.h>
#include <flags.h> #include <flags.h>
using namespace std::literals; using namespace std::literals;
@ -36,7 +35,8 @@ Advanced Options (Internal APIs):
--clone-attr SRC DEST clone permission, owner, and selinux context --clone-attr SRC DEST clone permission, owner, and selinux context
--clone SRC DEST clone SRC to DEST --clone SRC DEST clone SRC to DEST
--sqlite SQL exec SQL commands to Magisk database --sqlite SQL exec SQL commands to Magisk database
--use-broadcast use broadcast for su logging and notify --connect-mode [MODE] get/set connect mode for su request and notify
--broadcast-test manually trigger broadcast tests
Supported init triggers: Supported init triggers:
post-fs-data, service, boot-complete post-fs-data, service, boot-complete
@ -79,12 +79,10 @@ int magisk_main(int argc, char *argv[]) {
restore_rootcon(); restore_rootcon();
restorecon(); restorecon();
return 0; return 0;
} else if (argv[1] == "--clone-attr"sv) { } else if (argc >= 4 && argv[1] == "--clone-attr"sv) {;
if (argc < 4) usage();
clone_attr(argv[2], argv[3]); clone_attr(argv[2], argv[3]);
return 0; return 0;
} else if (argv[1] == "--clone"sv) { } else if (argc >= 4 && argv[1] == "--clone"sv) {
if (argc < 4) usage();
cp_afc(argv[2], argv[3]); cp_afc(argv[2], argv[3]);
return 0; return 0;
} else if (argv[1] == "--daemon"sv) { } else if (argv[1] == "--daemon"sv) {
@ -103,7 +101,7 @@ int magisk_main(int argc, char *argv[]) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, BOOT_COMPLETE); write_int(fd, BOOT_COMPLETE);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--sqlite"sv) { } else if (argc >= 3 && argv[1] == "--sqlite"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, SQLITE_CMD); write_int(fd, SQLITE_CMD);
write_string(fd, argv[2]); write_string(fd, argv[2]);
@ -115,14 +113,23 @@ int magisk_main(int argc, char *argv[]) {
printf("%s\n", res); printf("%s\n", res);
free(res); free(res);
} }
} else if (argv[1] == "--use-broadcast"sv) { } else if (argv[1] == "--connect-mode"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, BROADCAST_ACK); write_int(fd, BROADCAST_ACK);
return 0; if (argc >= 3) {
write_int(fd, parse_int(argv[2]));
} else {
write_int(fd, -1);
}
return read_int(fd);
} else if (argv[1] == "--remove-modules"sv) { } else if (argv[1] == "--remove-modules"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, REMOVE_MODULES); write_int(fd, REMOVE_MODULES);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--broadcast-test"sv) {
int fd = connect_daemon();
write_int(fd, BROADCAST_TEST);
return read_int(fd);
} }
#if 0 #if 0
/* Entry point for testing stuffs */ /* Entry point for testing stuffs */

View File

@ -19,6 +19,7 @@ enum {
SQLITE_CMD, SQLITE_CMD,
BROADCAST_ACK, BROADCAST_ACK,
REMOVE_MODULES, REMOVE_MODULES,
BROADCAST_TEST,
}; };
// Return codes for daemon // Return codes for daemon
@ -84,10 +85,13 @@ void magiskhide_handler(int client);
*************/ *************/
void su_daemon_handler(int client, struct ucred *credential); void su_daemon_handler(int client, struct ucred *credential);
void broadcast_test(); void broadcast_test(int client = -1);
void broadcast_ack(int client);
/*********************
* Daemon Global Vars
*********************/
extern int SDK_INT; extern int SDK_INT;
extern bool RECOVERY_MODE; extern bool RECOVERY_MODE;
extern bool CONNECT_BROADCAST;
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user") #define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user")

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