Merge remote-tracking branch 'john/master' into development

This commit is contained in:
Viktor De Pasquale 2019-05-02 14:45:28 +02:00
commit 1b4ae70a43
26 changed files with 522 additions and 646 deletions

View File

@ -38,6 +38,27 @@ Default string resources for Magisk Manager are scattered throughout
Translate each and place them in the respective locations (`<module>/src/main/res/values-<lang>/strings.xml`).
## Signature Verification
Official release zips and APKs are signed with my personal private key. You can verify the key certificate to make sure the binaries you downloaded are not manipulated in anyway.
``` bash
# Use the keytool command from JDK to print certificates
keytool -printcert -jarfile <APK or Magisk zip>
# The output should contain the following signature
Owner: CN=John Wu, L=Taipei, C=TW
Issuer: CN=John Wu, L=Taipei, C=TW
Serial number: 50514879
Valid from: Sun Aug 14 13:23:44 EDT 2016 until: Tue Jul 21 13:23:44 EDT 2116
Certificate fingerprints:
MD5: CE:DA:68:C1:E1:74:71:0A:EF:58:89:7D:AE:6E:AB:4F
SHA1: DC:0F:2B:61:CB:D7:E9:D3:DB:BE:06:0B:2B:87:0D:46:BB:06:02:11
SHA256: B4:CB:83:B4:DA:D9:9F:99:7D:BE:87:2F:01:3A:A1:6C:14:EE:C4:1D:16:70:21:F3:71:F7:E1:33:0F:27:3E:E6
Signature algorithm name: SHA256withRSA
Version: 3
```
## License
Magisk, including all git submodules are free software:

View File

@ -59,10 +59,6 @@ dependencies {
implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"
implementation "com.github.topjohnwu.libsu:io:${libsuVersion}"
def butterKnifeVersion = '10.1.0'
implementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}"
kapt "com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
def koin = "2.0.0-rc-2"
implementation "org.koin:koin-core:${koin}"
implementation "org.koin:koin-android:${koin}"

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk.di
import android.content.Intent
import android.net.Uri
import com.topjohnwu.magisk.ui.MainViewModel
import com.topjohnwu.magisk.ui.flash.FlashViewModel
@ -10,7 +9,6 @@ import com.topjohnwu.magisk.ui.log.LogViewModel
import com.topjohnwu.magisk.ui.module.ModuleViewModel
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
import com.topjohnwu.magisk.ui.surequest.SuRequestViewModel
import com.topjohnwu.magisk.ui.surequest._SuRequestViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
@ -23,8 +21,5 @@ val viewModelModules = module {
viewModel { ModuleViewModel(get(), get()) }
viewModel { LogViewModel(get(), get()) }
viewModel { (action: String, uri: Uri?) -> FlashViewModel(action, uri, get()) }
viewModel { (intent: Intent, action: String?) ->
_SuRequestViewModel(intent, action.orEmpty(), get(), get())
}
viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) }
}

View File

@ -1,5 +1,5 @@
package com.topjohnwu.magisk.model.entity.state
enum class IndeterminateState {
INDETERMINATE, CHECKED, UNCHECKED
}
CHECKED, INDETERMINATE, UNCHECKED
}

View File

@ -1,7 +1,7 @@
package com.topjohnwu.magisk.model.navigation
import com.topjohnwu.magisk.ui.hide.MagiskHideFragment
import com.topjohnwu.magisk.ui.home.MagiskFragment
import com.topjohnwu.magisk.ui.home.HomeFragment
import com.topjohnwu.magisk.ui.log.LogFragment
import com.topjohnwu.magisk.ui.module.ModulesFragment
import com.topjohnwu.magisk.ui.module.ReposFragment
@ -12,8 +12,8 @@ import com.topjohnwu.magisk.ui.superuser.SuperuserFragment
object Navigation {
fun home() = MagiskNavigationEvent {
navDirections { destination = MagiskFragment::class }
navOptions { popUpTo = MagiskFragment::class }
navDirections { destination = HomeFragment::class }
navOptions { popUpTo = HomeFragment::class }
}
fun superuser() = MagiskNavigationEvent {

View File

@ -12,6 +12,7 @@ import com.topjohnwu.magisk.databinding.ActivityMainBinding
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.base.MagiskActivity
import com.topjohnwu.magisk.ui.hide.MagiskHideFragment
import com.topjohnwu.magisk.ui.home.HomeFragment
import com.topjohnwu.magisk.ui.log.LogFragment
import com.topjohnwu.magisk.ui.module.ModulesFragment
import com.topjohnwu.magisk.ui.module.ReposFragment
@ -22,7 +23,6 @@ import com.topjohnwu.net.Networking
import com.topjohnwu.superuser.Shell
import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.reflect.KClass
import com.topjohnwu.magisk.ui.home.MagiskFragment as HomeFragment
open class MainActivity : MagiskActivity<MainViewModel, ActivityMainBinding>() {

View File

@ -14,6 +14,7 @@ import com.topjohnwu.magisk.model.entity.HideAppInfo
import com.topjohnwu.magisk.model.entity.HideTarget
import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem
import com.topjohnwu.magisk.model.entity.recycler.HideRvItem
import com.topjohnwu.magisk.model.entity.state.IndeterminateState
import com.topjohnwu.magisk.model.events.HideProcessEvent
import com.topjohnwu.magisk.ui.base.MagiskViewModel
import com.topjohnwu.magisk.utils.Utils
@ -72,7 +73,8 @@ class HideViewModel(
.filter { it.processes.isNotEmpty() }
.map { HideRvItem(it, hideTargets.blockingGet()) }
.toList()
.map { it.sortBy { it.item.info.name }; it }
.map { it.sortWith(compareBy(
{it.isHiddenState.value}, {it.item.info.name}, {it.packageName})); it }
.doOnSuccess { allItems.update(it) }
.flatMap { queryRaw() }
.applyViewModel(this)
@ -94,7 +96,10 @@ class HideViewModel(
it.item.name.contains(query, ignoreCase = true) ||
it.item.processes.any { it.contains(query, ignoreCase = true) }
}
.filter { if (showSystem) true else it.item.info.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
.filter {
showSystem || (it.isHiddenState.value != IndeterminateState.UNCHECKED) ||
(it.item.info.flags and ApplicationInfo.FLAG_SYSTEM == 0)
}
.toList()
.map { it to items.calculateDiff(it) }

View File

@ -1,24 +1,22 @@
package com.topjohnwu.magisk.ui.home
import android.app.Activity
import com.skoumal.teanity.viewevents.ViewEvent
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.databinding.FragmentMagiskBinding
import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.ui.base.MagiskActivity
import com.topjohnwu.magisk.ui.base.MagiskFragment
import com.topjohnwu.magisk.utils.ISafetyNetHelper
import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.magisk.view.SafetyNet
import com.topjohnwu.magisk.view.SafetyNet.EXT_APK
import com.topjohnwu.magisk.view.dialogs.*
import com.topjohnwu.net.Networking
import com.topjohnwu.superuser.Shell
import dalvik.system.DexClassLoader
import org.koin.androidx.viewmodel.ext.android.viewModel
import com.topjohnwu.magisk.ui.base.MagiskFragment as NewMagiskFragment
import java.io.File
class MagiskFragment : NewMagiskFragment<HomeViewModel, FragmentMagiskBinding>(),
class HomeFragment : MagiskFragment<HomeViewModel, FragmentMagiskBinding>(),
ISafetyNetHelper.Callback {
override val layoutRes: Int = R.layout.fragment_magisk
@ -83,7 +81,16 @@ class MagiskFragment : NewMagiskFragment<HomeViewModel, FragmentMagiskBinding>()
private fun updateSafetyNet(dieOnError: Boolean) {
try {
SafetyNet.dyRun(requireActivity(), this)
val loader = DexClassLoader(EXT_APK.path, EXT_APK.parent, null,
ISafetyNetHelper::class.java.classLoader)
val clazz = loader.loadClass("com.topjohnwu.snet.Snet")
val helper = clazz.getMethod("newHelper",
Class::class.java, String::class.java, Activity::class.java, Any::class.java)
.invoke(null, ISafetyNetHelper::class.java, EXT_APK.path,
requireActivity(), this) as ISafetyNetHelper
if (helper.version < Const.SNET_EXT_VER)
throw Exception()
helper.attest()
} catch (e: Exception) {
if (dieOnError) {
viewModel.finishSafetyNetCheck(-1)
@ -94,5 +101,9 @@ class MagiskFragment : NewMagiskFragment<HomeViewModel, FragmentMagiskBinding>()
downloadSafetyNet(!dieOnError)
}
}
companion object {
val EXT_APK = File("${App.self.filesDir.parent}/snet", "snet.apk")
}
}

View File

@ -1,166 +0,0 @@
package com.topjohnwu.magisk.ui.surequest
import android.hardware.fingerprint.FingerprintManager
import android.os.CountDownTimer
import android.text.SpannableStringBuilder
import android.widget.Toast
import androidx.core.text.bold
import com.skoumal.teanity.viewevents.ViewEvent
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ActivitySuRequestBinding
import com.topjohnwu.magisk.model.entity.Policy
import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.model.events.SuDialogEvent
import com.topjohnwu.magisk.ui.base.MagiskActivity
import com.topjohnwu.magisk.utils.FingerprintHelper
import com.topjohnwu.magisk.utils.feature.WIP
import com.topjohnwu.magisk.view.MagiskDialog
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit.SECONDS
@WIP
open class _SuRequestActivity : MagiskActivity<_SuRequestViewModel, ActivitySuRequestBinding>() {
override val layoutRes: Int = R.layout.activity_su_request
override val viewModel: _SuRequestViewModel by viewModel {
parametersOf(intent, intent.action)
}
//private val timeoutPrefs: SharedPreferences by inject(SUTimeout)
private val canUseFingerprint get() = FingerprintHelper.useFingerprint()
private val countdown by lazy {
val seconds = Config.get<Int>(Config.Key.SU_REQUEST_TIMEOUT).toLong()
val millis = SECONDS.toMillis(seconds)
object : CountDownTimer(millis, 1000) {
override fun onFinish() {
viewModel.deny()
}
override fun onTick(millisUntilFinished: Long) {
dialog.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
Timber.e("Tick, tock")
title = "%s (%d)".format(
getString(R.string.deny),
MILLISECONDS.toSeconds(millisUntilFinished)
)
}
}
}
}
private var fingerprintHelper: SuFingerprint? = null
private lateinit var dialog: MagiskDialog
override fun onEventDispatched(event: ViewEvent) {
super.onEventDispatched(event)
when (event) {
is SuDialogEvent -> showDialog(event.policy)
is DieEvent -> finish()
}
}
override fun onBackPressed() {
if (::dialog.isInitialized && dialog.isShowing) {
return
}
super.onBackPressed()
}
override fun onDestroy() {
if (this::dialog.isInitialized && dialog.isShowing) {
dialog.dismiss()
}
fingerprintHelper?.cancel()
countdown.cancel()
super.onDestroy()
}
private fun showDialog(policy: Policy) {
val titleText = SpannableStringBuilder("Allow ")
.bold { append(policy.appName) }
.append(" to access superuser rights?")
val messageText = StringBuilder()
.appendln(policy.packageName)
.append(getString(R.string.su_warning))
dialog = MagiskDialog(this)
.applyIcon(policy.info.loadIcon(packageManager))
.applyTitle(titleText)
.applyMessage(messageText)
//.applyView()) {} //todo add a spinner
.cancellable(false)
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.grant
onClick { viewModel.grant() }
if (canUseFingerprint) {
icon = R.drawable.ic_fingerprint
}
}
.applyButton(MagiskDialog.ButtonType.NEUTRAL) {
title = "%s %s".format(getString(R.string.grant), getString(R.string.once))
onClick { viewModel.grant(-1) }
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.deny
onClick { viewModel.deny() }
}
.onDismiss { finish() }
.onShow {
startTimer().also { Timber.e("Starting timer") }
if (canUseFingerprint) {
startFingerprintQuery()
}
}
.reveal()
}
private fun startTimer() {
countdown.start()
}
private fun startFingerprintQuery() {
val result = runCatching {
fingerprintHelper = SuFingerprint().apply { authenticate() }
}
if (result.isFailure) {
dialog.applyButton(MagiskDialog.ButtonType.POSITIVE) {
icon = 0
}
}
}
private inner class SuFingerprint @Throws(Exception::class)
internal constructor() : FingerprintHelper() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
Toast.makeText(this@_SuRequestActivity, errString, Toast.LENGTH_LONG).show()
}
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
Toast.makeText(this@_SuRequestActivity, helpString, Toast.LENGTH_LONG).show()
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
viewModel.grant()
}
override fun onAuthenticationFailed() {
Toast.makeText(this@_SuRequestActivity, R.string.auth_fail, Toast.LENGTH_LONG).show()
}
}
companion object {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
}
}

View File

@ -1,112 +0,0 @@
package com.topjohnwu.magisk.ui.surequest
import android.content.Intent
import android.content.pm.PackageManager
import com.skoumal.teanity.extensions.subscribeK
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.data.database.MagiskDB
import com.topjohnwu.magisk.model.entity.Policy
import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.model.events.SuDialogEvent
import com.topjohnwu.magisk.ui.base.MagiskViewModel
import com.topjohnwu.magisk.utils.SuConnector
import com.topjohnwu.magisk.utils.SuLogger
import com.topjohnwu.magisk.utils.feature.WIP
import com.topjohnwu.magisk.utils.now
import io.reactivex.Single
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit.MINUTES
@WIP
class _SuRequestViewModel(
intent: Intent,
action: String,
private val packageManager: PackageManager,
private val database: MagiskDB
) : MagiskViewModel() {
private val connector: Single<SuConnector> = Single.fromCallable {
val socketName = intent.extras?.getString("socket") ?: let {
deny()
throw IllegalStateException("Socket is empty or null")
}
object : SuConnector(socketName) {
override fun onResponse() {
policy.subscribeK { out.writeInt(it.policy) } //this just might be incorrect, lol
}
} as SuConnector
}.cache()
private val policy: Single<Policy> = connector.map {
val bundle = it.readSocketInput() ?: throw IllegalStateException("Socket bundle is null")
val uid = bundle.getString("uid")?.toIntOrNull() ?: let {
deny()
throw IllegalStateException("UID is empty or null")
}
database.clearOutdated()
database.getPolicy(uid) ?: Policy(uid, packageManager)
}.cache()
init {
when (action) {
SuRequestActivity.LOG -> SuLogger.handleLogs(intent).also { die() }
SuRequestActivity.NOTIFY -> SuLogger.handleNotify(intent).also { die() }
SuRequestActivity.REQUEST -> process()
else -> back() // invalid action, should ignore
}
}
private fun process() {
policy.subscribeK(onError = ::deny) { process(it) }
}
private fun process(policy: Policy) {
if (policy.packageName == BuildConfig.APPLICATION_ID)
deny().also { return }
if (policy.policy != Policy.INTERACTIVE)
grant().also { return }
when (Config.get<Int>(Config.Key.SU_AUTO_RESPONSE)) {
Config.Value.SU_AUTO_DENY -> deny().also { return }
Config.Value.SU_AUTO_ALLOW -> grant().also { return }
}
requestDialog(policy)
}
fun deny(e: Throwable? = null) = updatePolicy(Policy.DENY, 0).also { Timber.e(e) }
fun grant(time: Long = 0) = updatePolicy(Policy.ALLOW, time)
private fun updatePolicy(action: Int, time: Long) {
fun finish(e: Throwable? = null) = die().also { Timber.e(e) }
policy
.map { it.policy = action; it }
.doOnSuccess {
if (time >= 0) {
it.until = if (time == 0L) {
0
} else {
MILLISECONDS.toSeconds(now) + MINUTES.toSeconds(time)
}
database.updatePolicy(it)
}
}
.flatMap { connector }
.subscribeK(onError = ::finish) {
it.response()
finish()
}
}
private fun requestDialog(policy: Policy) {
SuDialogEvent(policy).publish()
}
private fun die() = DieEvent().publish()
}

View File

@ -1,161 +0,0 @@
package com.topjohnwu.magisk.view;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.topjohnwu.magisk.App;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import androidx.annotation.StringRes;
import androidx.cardview.widget.CardView;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.OnClick;
import butterknife.Unbinder;
import dalvik.system.DexClassLoader;
public class SafetyNet implements ISafetyNetHelper.Callback {
public static final File EXT_APK =
new File(App.self.getFilesDir().getParent() + "/snet", "snet.apk");
/*@BindView(R.id.safetyNet_card) */ CardView safetyNetCard;
@BindView(R.id.safetyNet_refresh) ImageView safetyNetRefreshIcon;
@BindView(R.id.safetyNet_status) TextView safetyNetStatusText;
@BindView(R.id.safetyNet_check_progress) ProgressBar safetyNetProgress;
@BindView(R.id.safetyNet_expand) ViewGroup expandLayout;
@BindView(R.id.cts_status_icon) ImageView ctsStatusIcon;
@BindView(R.id.cts_status) TextView ctsStatusText;
@BindView(R.id.basic_status_icon) ImageView basicStatusIcon;
@BindView(R.id.basic_status) TextView basicStatusText;
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
public Unbinder unbinder;
private final ExpandableViewHolder expandable;
public SafetyNet(View v) {
unbinder = new SafetyNet_ViewBinding(this, v);
expandable = new ExpandableViewHolder(expandLayout);
Context context = v.getContext();
safetyNetCard.setVisibility(hasGms(context) && Networking.checkNetworkStatus(context) ?
View.VISIBLE : View.GONE);
}
public static void dyRun(Activity activity, Object callback) throws Exception {
DexClassLoader loader = new DexClassLoader(EXT_APK.getPath(), EXT_APK.getParent(),
null, ISafetyNetHelper.class.getClassLoader());
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.Snet");
ISafetyNetHelper helper = (ISafetyNetHelper) clazz.getMethod("newHelper",
Class.class, String.class, Activity.class, Object.class)
.invoke(null, ISafetyNetHelper.class, EXT_APK.getPath(), activity, callback);
if (helper.getVersion() < Const.SNET_EXT_VER)
throw new Exception();
helper.attest();
}
public void reset() {
safetyNetStatusText.setText(R.string.safetyNet_check_text);
expandable.setExpanded(false);
}
@Override
public void onResponse(int response) {
safetyNetProgress.setVisibility(View.GONE);
safetyNetRefreshIcon.setVisibility(View.VISIBLE);
if ((response & 0x0F) == 0) {
safetyNetStatusText.setText(R.string.safetyNet_check_success);
boolean b;
b = (response & ISafetyNetHelper.CTS_PASS) != 0;
ctsStatusText.setText("ctsProfile: " + b);
ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
b = (response & ISafetyNetHelper.BASIC_PASS) != 0;
basicStatusText.setText("basicIntegrity: " + b);
basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
expandable.expand();
} else {
@StringRes int resid;
switch (response) {
case ISafetyNetHelper.RESPONSE_ERR:
resid = R.string.safetyNet_res_invalid;
break;
case ISafetyNetHelper.CONNECTION_FAIL:
default:
resid = R.string.safetyNet_api_error;
break;
}
safetyNetStatusText.setText(resid);
}
}
@OnClick(R.id.safetyNet_refresh)
void safetyNet(View v) {
Runnable task = () -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetRefreshIcon.setVisibility(View.INVISIBLE);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
check((Activity) v.getContext());
expandable.collapse();
};
if (!EXT_APK.exists()) {
// Show dialog
new CustomAlertDialog(v.getContext())
.setTitle(R.string.proprietary_title)
.setMessage(R.string.proprietary_notice)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> task.run())
.setNegativeButton(R.string.no_thanks, null)
.show();
} else {
task.run();
}
}
private void check(Activity activity) {
try {
dyRun(activity, this);
} catch (Exception ignored) {
Shell.sh("rm -rf " + EXT_APK.getParent()).exec();
EXT_APK.getParentFile().mkdir();
Networking.get(Const.Url.SNET_URL).getAsFile(EXT_APK, f -> {
try {
dyRun(activity, this);
} catch (Exception e) {
e.printStackTrace();
onResponse(-1);
}
});
}
}
private boolean hasGms(Context context) {
PackageManager pm = context.getPackageManager();
PackageInfo info;
try {
info = pm.getPackageInfo("com.google.android.gms", 0);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return info.applicationInfo.enabled;
}
}

View File

@ -4,18 +4,14 @@ import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AlertDialog;
import butterknife.BindView;
import com.topjohnwu.magisk.databinding.AlertDialogBinding;
public class CustomAlertDialog extends AlertDialog.Builder {
@ -24,32 +20,16 @@ public class CustomAlertDialog extends AlertDialog.Builder {
private DialogInterface.OnClickListener neutralListener;
protected AlertDialog dialog;
protected ViewHolder vh;
public class ViewHolder {
@BindView(R.id.dialog_layout) public LinearLayout dialogLayout;
@BindView(R.id.button_panel) public LinearLayout buttons;
@BindView(R.id.message) public TextView messageView;
@BindView(R.id.negative) public Button negative;
@BindView(R.id.positive) public Button positive;
@BindView(R.id.neutral) public Button neutral;
ViewHolder(View v) {
new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
messageView.setVisibility(View.GONE);
negative.setVisibility(View.GONE);
positive.setVisibility(View.GONE);
neutral.setVisibility(View.GONE);
buttons.setVisibility(View.GONE);
}
}
protected AlertDialogBinding binding;
{
View v = LayoutInflater.from(getContext()).inflate(R.layout.alert_dialog, null);
vh = new ViewHolder(v);
super.setView(v);
binding = AlertDialogBinding.inflate(LayoutInflater.from(getContext()));
super.setView(binding.getRoot());
binding.message.setVisibility(View.GONE);
binding.negative.setVisibility(View.GONE);
binding.positive.setVisibility(View.GONE);
binding.neutral.setVisibility(View.GONE);
binding.buttonPanel.setVisibility(View.GONE);
}
public CustomAlertDialog(@NonNull Context context) {
@ -60,10 +40,6 @@ public class CustomAlertDialog extends AlertDialog.Builder {
super(context, themeResId);
}
public ViewHolder getViewHolder() {
return vh;
}
@Override
public CustomAlertDialog setView(int layoutResId) { return this; }
@ -72,8 +48,8 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@Override
public CustomAlertDialog setMessage(@Nullable CharSequence message) {
vh.messageView.setVisibility(View.VISIBLE);
vh.messageView.setText(message);
binding.message.setVisibility(View.VISIBLE);
binding.message.setText(message);
return this;
}
@ -84,11 +60,11 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@Override
public CustomAlertDialog setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
vh.buttons.setVisibility(View.VISIBLE);
vh.positive.setVisibility(View.VISIBLE);
vh.positive.setText(text);
binding.buttonPanel.setVisibility(View.VISIBLE);
binding.positive.setVisibility(View.VISIBLE);
binding.positive.setText(text);
positiveListener = listener;
vh.positive.setOnClickListener(v -> {
binding.positive.setOnClickListener(v -> {
if (positiveListener != null) {
positiveListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
}
@ -104,11 +80,11 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@Override
public CustomAlertDialog setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
vh.buttons.setVisibility(View.VISIBLE);
vh.negative.setVisibility(View.VISIBLE);
vh.negative.setText(text);
binding.buttonPanel.setVisibility(View.VISIBLE);
binding.negative.setVisibility(View.VISIBLE);
binding.negative.setText(text);
negativeListener = listener;
vh.negative.setOnClickListener(v -> {
binding.negative.setOnClickListener(v -> {
if (negativeListener != null) {
negativeListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
}
@ -124,11 +100,11 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@Override
public CustomAlertDialog setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
vh.buttons.setVisibility(View.VISIBLE);
vh.neutral.setVisibility(View.VISIBLE);
vh.neutral.setText(text);
binding.buttonPanel.setVisibility(View.VISIBLE);
binding.neutral.setVisibility(View.VISIBLE);
binding.neutral.setText(text);
neutralListener = listener;
vh.neutral.setOnClickListener(v -> {
binding.neutral.setOnClickListener(v -> {
if (neutralListener != null) {
neutralListener.onClick(dialog, DialogInterface.BUTTON_NEUTRAL);
}

View File

@ -36,9 +36,9 @@ public class FingerprintAuthDialog extends CustomAlertDialog {
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
fingerprint.setTint(ta.getColor(0, Color.GRAY));
ta.recycle();
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
vh.messageView.setGravity(Gravity.CENTER);
binding.message.setCompoundDrawables(null, null, null, fingerprint);
binding.message.setCompoundDrawablePadding(Utils.dpInPx(20));
binding.message.setGravity(Gravity.CENTER);
setMessage(R.string.auth_fingerprint);
setNegativeButton(R.string.close, (d, w) -> {
helper.cancel();
@ -81,20 +81,20 @@ public class FingerprintAuthDialog extends CustomAlertDialog {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(errString);
binding.message.setTextColor(Color.RED);
binding.message.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(helpString);
binding.message.setTextColor(Color.RED);
binding.message.setText(helpString);
}
@Override
public void onAuthenticationFailed() {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(R.string.auth_fail);
binding.message.setTextColor(Color.RED);
binding.message.setText(R.string.auth_fail);
}
@Override

View File

@ -1,63 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/dialog_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="5dp">
<TextView
android:id="@+id/message"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="25dp"
android:paddingTop="12dp"
android:paddingEnd="25dp"
android:paddingBottom="12dp"
android:textColor="?android:attr/textColorPrimary" />
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:id="@+id/button_panel"
style="?android:attr/buttonBarStyle"
android:id="@+id/dialog_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:measureWithLargestChild="true"
android:minHeight="54dp"
android:orientation="horizontal"
android:padding="2dp">
android:orientation="vertical"
android:paddingTop="5dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/negative"
style="@style/Widget.Button.Text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layout_weight="1"
android:maxLines="2"
tools:text="Negative" />
<TextView
android:id="@+id/message"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="25dp"
android:paddingTop="12dp"
android:paddingEnd="25dp"
android:paddingBottom="12dp"
android:textColor="?android:attr/textColorPrimary" />
<com.google.android.material.button.MaterialButton
android:id="@+id/neutral"
style="@style/Widget.Button.Text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:maxLines="2"
tools:text="Neutral" />
<LinearLayout
android:id="@+id/button_panel"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:measureWithLargestChild="true"
android:minHeight="54dp"
android:orientation="horizontal"
android:padding="2dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/positive"
style="@style/Widget.Button.Text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_weight="1"
android:maxLines="2"
tools:text="Positive" />
<com.google.android.material.button.MaterialButton
android:id="@+id/negative"
style="@style/Widget.Button.Text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layout_weight="1"
android:maxLines="2"
tools:text="Negative" />
<com.google.android.material.button.MaterialButton
android:id="@+id/neutral"
style="@style/Widget.Button.Text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:maxLines="2"
tools:text="Neutral" />
<com.google.android.material.button.MaterialButton
android:id="@+id/positive"
style="@style/Widget.Button.Text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_weight="1"
android:maxLines="2"
tools:text="Positive" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</layout>

View File

@ -1,9 +1,3 @@
# 7.1.1
- Fix a bug that causes some modules using new format not showing up
# v7.1.0
- Support the new module format
- Support per-application component granularity MagiskHide targets (only on v19+)
- Ask for fingerprint before deleting rules if enabled
- Fix the bug that causes repackaging to lose settings
- Several UI fixes
# v7.1.2
- Support patching Samsung AP firmware
- Much better module downloading mechanism

View File

@ -160,7 +160,7 @@
<string name="auto_response">Automatická odezva</string>
<string name="request_timeout">Časový limit požadavku</string>
<string name="superuser_notification">Oznámení Superuser</string>
<string name="request_timeout_summary">%1$s sekund</string>
<string name="request_timeout_summary">%1$d sekund</string>
<string name="settings_su_reauth_title">Opětovné ověření po aktualizaci</string>
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění Superuser po aktualizaci aplikace</string>
<string name="settings_su_fingerprint_title">Povolit ověřování otisky prstů</string>

View File

@ -0,0 +1,225 @@
<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="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">Сите модули ќе бидат оневозможени/oтстранети. Рут привилегиите ќе бидат отстранети, а вашите податоци може да се енкриптираат ако моментално не се.</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">Рестартирај во Recovery режим</string>
<string name="reboot_bootloader">Рестартирај во Bootloader режим</string>
<string name="reboot_download">Рестартирај во Download режим</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>
<string name="log_is_empty">Записникот е празен.</string>
<!--About Activity-->
<string name="app_changelog">Листа на промени</string>
<string name="translators">davidtrpcevski</string>
<string name="app_translators">Преведувачи</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="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">Инсталирај во неактивен слот (по ОТА)</string>
<string name="install_inactive_slot_msg">Вашиот уред ќе биде ПРИНУДЕН да се подигне на тековниот неактивен слот по рестартирањето!\nОваа опција користете ја само откако OTA ажурирање е направено.\nПродолжи?</string>
<string name="select_method">Избери метод</string>
<string name="setup_title">Дополнителни подесувања</string>
<string name="select_patch_file">Избери и обнови ја датотеката</string>
<string name="patch_file_msg">Избери само image датотека (*.img) или ODIN tar датотека (*.tar)</string>
<string name="reboot_delay_toast">Рестартирање за 5 секунди…</string>
<!--Toasts, Dialogs-->
<string name="close">Затвори</string>
<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">Кешот од репозиториумот е исчистен</string>
<string name="internal_storage">Zip датотеката е сместена во:\n[Внатрешна меморија]%1$s</string>
<string name="dtbo_patched_title">DTBO е обновено!</string>
<string name="dtbo_patched_reboot">Magisk Manager ја обнови dtbo.img датотеката. Ве молиме, рестартирајте.</string>
<string name="flashing">Применувам</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">Врати image датотеки</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 апликација и затоа не содржи код од SafetyNet API кој е во сопственост на Google.\n\nДали ќе дозволите Magisk Manager да преземе проширување (содржи GoogleApiClient) за SafetyNet проверка?</string>
<string name="setup_fail">Поставувањето е неуспешно.</string>
<string name="env_fix_title">Потребни е дополнително поставување</string>
<string name="env_fix_msg">На Вашиот уред му е потребно дополнително поставување за Magisk да работи нормално. Ќе се преземе ZIP фајлот за поставување на Magisk, дали сакате да продолжите?</string>
<string name="setup_msg">Надградба на работната околина…</string>
<string name="downloading_toast">Преземање %1$s</string>
<string name="no_rw_storage">Оваа функција нема да работи без дозвола за запис на надворешната меморија.</string>
<string name="dl_one_module">Преземај модули еден по друг.</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_clear_cache_title">Исчисти го кешот од репозиториумот</string>
<string name="settings_clear_cache_summary">Исчисти ги кешираните информации за онлајн репозиториумот. Ова ја принудува апликацијата да се освежи онлајн.</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 Core основен режим</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">Поддршка за несистемски хостови за Adblock апликации.</string>
<string name="settings_hosts_toast">Додаден е модул за несистемски хостови</string>
<string name="settings_su_app_adb">Апликации и АДБ</string>
<string name="settings_su_app">Само апликации</string>
<string name="settings_su_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="android_o_not_support">Не е поддржано на Android 8.0+.</string>
<string name="disable_fingerprint">Нема регистрирано отпечатоци од прсти или уредот не ја поддржува оваа функција.</string>
<!--Superuser-->
<string name="su_request_title">Супер-корисник барање</string>
<string name="deny_with_str">Одбиј%1$s</string>
<string name="deny">Одбиј</string>
<string name="prompt">Прашај</string>
<string name="grant">Одобри</string>
<string name="su_warning">Дава целосен пристап на вашиот уред.\nОдбијте ако не сте сигурни!</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_snack_revoke">Правата за %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">Команда: %1$s</string>
<!-- MagiskHide -->
<string name="show_system_app">Прикажи ги системските апликации</string>
</resources>

View File

@ -63,7 +63,7 @@
<!--About Activity-->
<string name="app_changelog">Список изменений</string>
<string name="translators">Displax [4PDA], igor-dyatlov, zertyuiop</string>
<string name="translators" />
<string name="app_translators">Переводчики</string>
<!-- System Components, Notifications -->
@ -74,6 +74,18 @@
<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">Ваше устройство будет принудительно перезагружено в неактивный слот!\nИспользуйте эту опцию только при установке OTA.\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 (*.tar)</string>
<string name="reboot_delay_toast">Перезагрузка через 5 секунд…</string>
<!--Toasts, Dialogs-->
<string name="close">Закрыть</string>
<string name="repo_install_title">Установка %1$s</string>
@ -84,19 +96,14 @@
<string name="release_notes">О версии</string>
<string name="repo_cache_cleared">Кэш репозитория очищен</string>
<string name="internal_storage">Расположение архива:\n[Внутреннее Хранилище]%1$s</string>
<string name="manager_download_install">Нажмите, чтобы загрузить и установить.</string>
<string name="dtbo_patched_title">DTBO пропатчен!</string>
<string name="dtbo_patched_reboot">Magisk Manager пропатчил dtbo.img. Перезагрузите устройство.</string>
<string name="flashing">Прошивка</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="download_zip_only">Загрузка установочного ZIP</string>
<string name="direct_install">Прямая установка (Рекомендуется)</string>
<string name="install_inactive_slot">Установка в неактивный слот (После OTA)</string>
<string name="open_link_failed_toast">Не найдено приложений для открытия ссылки.</string>
<string name="warning">Предупреждение</string>
<string name="install_inactive_slot_msg">Ваше устройство будет принудительно перезагружено в неактивный слот!\nИспользуйте эту опцию только при установке OTA.\nПродолжить?</string>
<string name="select_method">Выбор способа</string>
<string name="complete_uninstall">Полное удаление</string>
<string name="restore_img">Восстановить разделы</string>
<string name="restore_img_msg">Восстановление…</string>
@ -107,7 +114,6 @@
<string name="setup_fail">Ошибка установки.</string>
<string name="env_fix_title">Требуется дополнительная установка</string>
<string name="env_fix_msg">Вашему устройству требуется дополнительная установка Magisk для корректной работы. Будет загружен установочный ZIP Magisk, продолжить?</string>
<string name="setup_title">Дополнительная установка</string>
<string name="setup_msg">Настройка рабочей среды…</string>
<string name="downloading_toast">Загрузка %1$s</string>
<string name="no_rw_storage">Требуется разрешение на запись во внешнее хранилище.</string>

View File

@ -63,7 +63,7 @@
<!--About Activity-->
<string name="app_changelog">Uygulama değişiklikleri</string>
<string name="translators">Mevlüt TOPÇU - Muhammet Emin TURGUT</string>
<string name="translators">Mevlüt TOPÇU - Muhammet Emin TURGUT - XORCAN</string>
<string name="app_translators">Çevirmenler</string>
<!-- System Components, Notifications -->
@ -74,6 +74,18 @@
<string name="magisk_update_title">Yeni Magisk Güncellemesi Mevcut!</string>
<string name="manager_update_title">Yeni Magisk Manager Güncellemesi Mevcut!</string>
<!-- Installation -->
<string name="manager_download_install">İndirmek ve yüklemek için tıklayın.</string>
<string name="download_zip_only">Sadece Zip\'i indir</string>
<string name="direct_install">Doğrudan kurulum (Önerilen)</string>
<string name="install_inactive_slot">İnaktif slota yükle (OTA\'dan sonra)</string>
<string name="install_inactive_slot_msg">Cihazınız, yeniden başlatmanın ardından inaktif slota ön yüklemek için ZORUNLU olacaktır!\nBu seçeneği yalnızca OTA yapıldıktan sonra kullanın.\nDevam?</string>
<string name="select_method">Yöntem seçin</string>
<string name="setup_title">Ek kurulum</string>
<string name="select_patch_file">Dosya seçin ve yamalayın</string>
<string name="patch_file_msg">Bir raw yansısı (*.img) ya da ODIN tarfile dosyası (*.tar) seçin</string>
<string name="reboot_delay_toast">5 saniye içinde yeniden başlatılacak...</string>
<!--Toasts, Dialogs-->
<string name="close">Kapat</string>
<string name="repo_install_title">%1$s yükle</string>
@ -84,19 +96,14 @@
<string name="release_notes">Sürüm notları</string>
<string name="repo_cache_cleared">Repo önbelleği temizlendi</string>
<string name="internal_storage">Zip şuraya depolandı:\n[Dahili Hafıza]%1$s</string>
<string name="manager_download_install">İndirmek ve yüklemek için dokunun</string>
<string name="dtbo_patched_title">DTBO yamalandı!</string>
<string name="dtbo_patched_reboot">Magisk Manager dtbo.img\'yi yamaladı, lütfen yeniden başlatın</string>
<string name="flashing">Yükleniyor</string>
<string name="hide_manager_title">Magisk Manager Gizleniyor…</string>
<string name="hide_manager_fail_toast">Magisk Manager\'ı Gizleme başarısız oldu…</string>
<string name="open_link_failed_toast">Bağlantıyı açabilecek uygulama bulunamadı</string>
<string name="download_zip_only">Yalnızca Zip Dosyasını İndir</string>
<string name="direct_install">Doğrudan Yükle (Önerilen)</string>
<string name="install_inactive_slot">Pasif yuvaya yükle (OTA\'dan sonra)</string>
<string name="warning">Uyarı</string>
<string name="install_inactive_slot_msg">Cihazınız yeniden başlatıldıktan sonra mevcut pasif yuvaya ZORLA önyüklenecek!\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.\nDevam mı?</string>
<string name="select_method">Yöntem Seçin</string>
<string name="complete_uninstall">Tamamen Kaldır</string>
<string name="restore_img">Önyükleme İmajını Geri Yükle</string>
<string name="restore_img_msg">Geri Yükleniyor…</string>
@ -106,8 +113,7 @@
<string name="proprietary_notice">Magisk Yöneticisi, FOSS olduğundan Google\'ın tescilli olduğu SafetyNet API kodunu içermez.\n\nMagisk Manager\'ın SafetyNet kontrolü için bir uzantıyı (GoogleApiClient içeriyor) indirmesine izin veriyor musunuz?</string>
<string name="setup_fail">Kurulum başarısız</string>
<string name="env_fix_title">Ek Kurulum Gerekli</string>
   <string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz?</string>
<string name="setup_title">Ek Kurulum</string>
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz?</string>
<string name="setup_msg">Ortam kurulumu çalışıyor…</string>
<string name="downloading_toast">%1$s indiriliyor</string>
<string name="no_rw_storage">Bu özellik harici depolamaya yazma izni olmadan çalışmaz.</string>
@ -127,7 +133,7 @@
<string name="system_default">(Sistem Varsayılanı)</string>
<string name="settings_update">Güncelleme Ayarları</string>
<string name="settings_check_update_title">Güncellemeleri denetle</string>
   <string name="settings_check_update_summary">Düzenli aralıklarla arka planda güncellemeleri denetle</string>
<string name="settings_check_update_summary">Düzenli aralıklarla arka planda güncellemeleri denetle</string>
<string name="settings_update_channel_title">Güncelleme Kanalı</string>
<string name="settings_update_stable">Kararlı</string>
<string name="settings_update_beta">Beta</string>
@ -188,10 +194,10 @@
<string name="su_warning">Cihazınıza tam erişim izni verir.\nEmin değilseniz, reddedin!</string>
<string name="forever">Daima</string>
<string name="once">Bir kere</string>
   <string name="tenmin">10 dakika</string>
   <string name="twentymin">20 dakika</string>
   <string name="thirtymin">30 dakika</string>
   <string name="sixtymin">60 dakika</string>
<string name="tenmin">10 dakika</string>
<string name="twentymin">20 dakika</string>
<string name="thirtymin">30 dakika</string>
<string name="sixtymin">60 dakika</string>
<string name="su_allow_toast">%1$s için yetkili kullanıcı hakları verildi</string>
<string name="su_deny_toast">%1$s için yetkili kullanıcı hakları reddedildi</string>
<string name="no_apps_found">Hiçbir uygulama bulunamadı</string>

View File

@ -12,11 +12,12 @@ buildscript {
google()
jcenter()
maven { url 'http://storage.googleapis.com/r8-releases/raw' }
maven { url 'https://kotlin.bintray.com/kotlinx' }
}
dependencies {
classpath 'com.android.tools:r8:1.4.79'
classpath 'com.android.tools:r8:1.4.93'
classpath 'com.android.tools.build:gradle:3.5.0-alpha13'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.30"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31"
// NOTE: Do not place your application dependencies here; they belong

View File

@ -1,10 +1,11 @@
# Magisk Documentations
(Updated on 2019.3.28)
(Updated on 2019.5.1)
- [Installation](install.md)
- [Prerequisite](install.md#prerequisite)
- [Custom Recovery](install.md#custom-recovery)
- [Boot Image Patching](install.md#boot-image-patching)
- [Samsung (System-as-root)](install.md#samsung-system-as-root)
- [Huawei](install.md#huawei)
- [Tutorials](tutorials.md)
- [OTA Installation](tutorials.md#ota-installation)

View File

@ -1,6 +1,8 @@
# Installation
If you already have Magisk installed, it is **strongly recommended to upgrade directly via Magisk Manager**.
The following tutorial is for first time users. For Huawei users, please check the specific section for more information.
If you already have Magisk installed, it is **strongly recommended to upgrade directly via Magisk Manager**. The following tutorial is for first time users.
- If you are using a Huawei device running **EMUI 8 and higher**, please check its own section.
- If you are using a Samsung device that is **launched with Android 9.0** (new devices in 2019), please check its own section.
## Prerequisite
- If you plan to install custom kernels, flash the zip **AFTER** installing Magisk
@ -17,43 +19,102 @@ If your device have custom recovery support, the easiest way is to install it th
## Boot Image Patching
This is the "cool" way to install Magisk on your device. Either your device does not have proper custom recoveries, your device is using the A/B partition scheme and you don't want to mix recovery and boot images together, or you have other concerns (e.g. [OTA Installation](tutorials.md#ota-installation)), you should use this method instead.
In order to use this method, you are required to obtain a copy of the stock boot image, which can be found by extracting OEM provided factory images, or extracted from OTA update zips. If you are unable to obtain one yourself, someone on the Internet might share it somewhere. The following instructions will guide you through the process after you have the copy of boot image.
In order to use this method, you are required to obtain a copy of the stock boot image, which can be found by extracting OEM provided factory images or extracting from OTA update zips. If you are unable to obtain one yourself, someone on the Internet might share it somewhere. The following instructions will guide you through the process after you have the copy of boot image.
- Copy the boot image to your device
- Download and install the latest Magisk Manager
- If you're planning to flash the patched boot image through ODIN (Samsung only), go to **Settings > Update Settings > Patched Boot Output Format**, and select *.img.tar*, or else leave it as the default (*.img*)
- Press **Install > Install > Patch Boot Image File**, and select your stock boot image file
- Magisk Manager will install Magisk to your boot image, and store it in
`[Internal Storage]/Download/patched_boot.img[.tar]`
- Copy the patched boot image from your device to your PC. If you can't find it via MTP, you can pull the file with ADB:
`adb pull /sdcard/Download/patched_boot.img[.tar]`
- Flash the patched boot image to your device and reboot. Here is the command if using fastboot on most devices:
`fastboot flash boot /path/to/patched_boot.img`
- Press **Install → Install → Select and Patch a File**, and select your stock boot image file
- Magisk Manager will install Magisk to your boot image, and store it in `[Internal Storage]/Download/magisk_patched.img`
- Copy the patched boot image from your device to your PC. If you can't find it via MTP, you can pull the file with ADB: <br>
`adb pull /sdcard/Download/magisk_patched.img`
- Flash the patched boot image to your device and reboot. Here is the command if using fastboot on most devices: <br>
`fastboot flash boot /path/to/magisk_patched.img`
## Magisk in Recovery
Due to the fact that some devices no longer uses ramdisk in boot images, Magisk has no choice but to be installed in the recovery partition. For these devices, you will have to **boot to recovery every time** if you want Magisk. Since both Magisk and recovery lives in the same partition, what you actually end up getting when you choose to boot to recovery will be determined by **how long you press volume up**.
Each OEM has its own key combo to boot into recovery. For example on Samsung it is **(Power + Bixby + Volume Up)**, and for Huawei it is **(Power + Volume Up)**. As soon as you press the combo and the device vibrates with a splash screen, the bootloader has already chosen which mode it is booting, either it be `boot`, `recovery`, or some OEM specific modes like `download`, `fastboot`, or `erecovery`. After the splash screen, release all buttons to boot into Magisk, since by default `recovery` mode will boot to the system with Magisk enabled. If you decide to boot to actual recovery, continue to press volume up until you see the recovery screen.
**In summary, after installing Magisk:**
- **(Powering up normally) → (System with NO Magisk)**
- **(OEM Recovery Key Combo) → (Splash screen) → (Release all buttons) → (System with Magisk)**
- **(OEM Recovery Key Combo) → (Splash screen) → (Keep pressing volume up) → (Actual recovery)**
## Samsung (System-as-root)
**If your device is NOT launched with Android 9.0 or higher (released after 2019), follow the normal tutorial**
### Before Installing Magisk
- Installing Magisk **WILL** trip KNOX
- Installing Magisk for the first time **REQUIRES** a full data wipe, backup before continue
- You have to have your bootloader unlocked before following the instructions
- Magisk will be installed to the **recovery** partition of your device. **Please read the Magisk in Recovery section before following the instructions below!**
- After installing Magisk, you can directly upgrade Magisk within Magisk Manager without an issue. **Flashing in custom recovery is not supported for now.**
### Unlocking Bootloader
Normally I won't provide instructions for this, but since things had changed drastically from previous Samsung devices, and there are some details that many might not know, I figure this would be helpful.
- Allow bootloader unlocking in Developer options → OEM unlocking
- Power off your device. Press *Bixby + Volume Down* and plug in your device to a PC to boot into download mode
- Long press volume up to unlock the bootloader. **This will wipe your data and automatically reboot.**
Just when you think the bootloader is unlocked, surprise surprise, it is *actually* not! Samsung introduced `VaultKeeper` in the system, meaning the bootloader will reject any unofficial partitions before `VaultKeeper` explicitly allows it.
- Go through the initial setup. Skip through all the steps since data will be wiped again later when we are installing Magisk. **Connect the device to internet in the setup!**
- Enable developer options, and **confirm that the OEM unlocking option exists and grayed out!** The `VaultKeeper` service will unleash the bootloader after it confirms that the user has the OEM unlocking option enabled. This step is to simply make sure the service gets the correct info, and also double check that our device is in a correct state
- Your bootloader now accepts unofficial images in download mode.
### Instructions
1. Download the firmware for your device.
2. Unzip the firmware and copy the **AP** tar file to your device. It is normally named as `AP_[device_model_sw_ver].tar.md5`
3. Install the latest Magisk Manager
4. In Magisk Manager: **Install → Install → Select and Patch a File** and select the AP tar file.
5. Magisk Manager will patch the whole firmware file and store the output to
`[Internal Storage]/Download/magisk_patched.tar`
6. Copy the tar file to your PC, and boot your device to download mode.
7. Flash `magisk_patched.tar` as AP in ODIN <br> **Important: Uncheck "Auto Reboot" in Options!!!!**
8. Magisk is now successfully flashed to your device! But there are still several steps before you can properly use the device.
9. We now want to boot into the stock recovery to factory reset our device. <br>
**Full data wipe is mandatory! Do not skip this step.** <br>
Press *Power + Volume Down* to exit download mode. As soon as the screen turns off, immediately press *Power + Bixby + Volume Up* to boot to recovery partition. Just as mentioned in the previous section, since we want to boot into stock recovery, **continue pressing the volume up button until you see the stock recovery screen**.
10. In the stock recovery menu, use volume buttons to navigate through menus, and the power button to select the option. Select *Wipe data/factory reset* to wipe the data of the device.
11. This time, we can finally boot to the system with Magisk. Select *Reboot system now*, and immediately press *Power + Bixby + Volume Up*. After seeing the bootloader warning screen, release all buttons so it can boot to the system.
12. The device will automatically reboot for the first time it boots. This is completely normal and done by design.
13. After the device is booted up, do the usual initial setup. **The following steps will need internet connection.**
14. You shall see Magisk Manager in your app drawer; if not, manually install the APK you downloaded in step 3 and continue to the next step. The app would be a stub and it shall automatically upgrade to the full Magisk Manager when you open it.
15. Magisk Manager will ask to do additional setups. Let it do its job and the app will automatically reboot your device.
16. Voila! Enjoy Magisk :)
### Additional Info
- Magisk actually patches 3 partitions on your device:
- `vbmeta`: replace with empty vbmeta image to disable partition verification
- `boot`: remove the signature of the image to prevent soft bricks
- `recovery`: this is where Magisk is actually installed
- **Never, ever** try to restore either of the 3 images mentioned back to stock! You can easily brick your device by doing so, and the only way out is to do full ODIN restore following with factory reset. Just don't do it.
- If you want to upgrade your device, **never** flash the stock **AP** tar file with reasons mentioned above. **Always** pre-patch the firmware before flashing in ODIN.
- If you don't need to patch the full firmware, you can manually create a tar file with **at least** `vbmeta.img`, `boot.img`, and `recovery.img` to let Magisk Manager patch your images in the proper way.
## Huawei
Huawei devices using Kirin processors have a different partitioning method from most common devices. Magisk is usually installed to the `boot` partition of the device, however Huawei devices does not have this partition. Depending on what EMUI version your device is running the instructions are slightly different. Even if you have switched to a custom ROM, you shall still know which version of EMUI you are running before switching.
Huawei devices using Kirin processors have a different partitioning method from most common devices. Magisk is usually installed to the `boot` partition of the device, however Huawei devices does not have this partition. Depending on what EMUI version your device is running, the instructions will be slightly different.
### Obtain Stock Images
Huawei does not release official factory images, however most firmware zips can be downloaded from the [Huawei Firmware Database](http://pro-teammt.ru/firmware-database/). To extract the images from `UPDATE.APP` in the zip, you have to use [Huawei Update Extractor](https://forum.xda-developers.com/showthread.php?t=2433454) (Windows only!)
Huawei does not release official factory images, however most firmware zips can be downloaded from the [Huawei Firmware Database](http://pro-teammt.ru/firmware-database/). To extract images from `UPDATE.APP` in the zip, you have to use [Huawei Update Extractor](https://forum.xda-developers.com/showthread.php?t=2433454) (Windows only!)
### EMUI 8
For EMUI 8 devices, your device have a partition named `ramdisk`, which will be where Magisk is going to be installed.
For EMUI 8 devices, your device have a partition named `ramdisk`, which is where Magisk is going to be installed.
- If you plan to use custom recoveries, simply follow the instructions for custom recovery above.
Note that to install TWRP, you will first download the TWRP recovery image, and use
`fastboot flash recovery_ramdisk /path/to/twrp.img` to install the custom recovery.
- If you plan not to use custom recoveries, you will have to extract `RAMDISK.img` from your firmware. Follow the instructions for boot image patching above, but use the `RAMDISK.img` file instead of a boot image. To install the patched image back to your device, here is the fastboot command:
`fastboot flash ramdisk /path/to/patched_boot.img`.
- If you plan to use custom recoveries, simply follow the instructions for custom recovery and you're all set.
- If you plan not to use custom recoveries, you will have to extract `RAMDISK.img` from your firmware. Follow the instructions for boot image patching above, but use the `RAMDISK.img` file instead of a boot image.
- To flash the patched image to your device, here is the fastboot command: <br>
`fastboot flash ramdisk /path/to/magisk_patched.img` <br>
Be aware you are flashing to `ramdisk`, not `boot`!
### EMUI 9
For EMUI 9 devices, the `ramdisk` partition no longer exists. As a workaround, Magisk will be installed to the `recovery_ramdisk` partition. **This means that you HAVE TO boot to recovery every time you reboot.** To boot to recovery, press **Power + Volume Up** when booting your device.
### EMUI 9 or Higher
For EMUI 9+ devices, the `ramdisk` partition no longer exists. As a workaround, Magisk will be installed to the `recovery_ramdisk` partition. **Please read the Magisk in Recovery section before following the instructions below!**
- If you plan to use custom recoveries, simply follow the instructions for custom recovery above.
Note that to install TWRP, you will first download the TWRP recovery image, and use
`fastboot flash recovery_ramdisk /path/to/twrp.img` to install the custom recovery.
**Magisk will overwrite the custom recovery.**
- If you plan not to use custom recoveries, you will have to extract `RECOVERY_RAMDIS.img` from your firmware. Follow the instructions for boot image patching above, but use the `RECOVERY_RAMDIS.img` file instead of a boot image. To install the patched image back to your device, here is the fastboot command:
`fastboot flash recovery_ramdisk /path/to/patched_boot.img`.
Be aware you are flashing to `recovery_ramdisk`, not `boot` nor `ramdisk`!
- You can still install custom recoveries to the `erecovery_ramdisk` partition. Boot to erecovery mode to boot into custom recovery in this case
*Note: As I tested on my Honor View 10, Huawei's kernel does not seem to be able to capture key button press events in early boot, so long pressing Volume Up does **NOT** boot to recovery on my device. Your experience may vary.*
- If you plan to use custom recoveries, simply follow the instructions for custom recovery and you're all set. <br>
**Warning: Magisk will overwrite the custom recovery.**
- If you plan not to use custom recoveries, you will have to extract `RECOVERY_RAMDIS.img` from your firmware. Follow the instructions for boot image patching above, but use the `RECOVERY_RAMDIS.img` file instead of a boot image.
- To flash the patched image to your device, here is the fastboot command: <br>
`fastboot flash recovery_ramdisk /path/to/magisk_patched.img` <br>
Be aware you are flashing to `recovery_ramdisk`, not `boot`!

View File

@ -609,10 +609,8 @@ void MagiskInit::setup_rootfs() {
sprintf(path, "/sbin/%s", applet_names[i]);
xsymlink("/sbin/magisk", path);
}
for (int i = 0; init_applet[i]; ++i) {
sprintf(path, "/sbin/%s", init_applet[i]);
xsymlink("/sbin/magiskinit", path);
}
xsymlink("/sbin/magiskinit", "/sbin/magiskpolicy");
xsymlink("/sbin/magiskinit", "/sbin/supolicy");
close(rootdir);
close(sbin);

View File

@ -0,0 +1,13 @@
<resources>
<!--Not translatable-->
<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"/>
<!--Used in both stub and full app-->
<string name="no_thanks">Не, благодарам</string>
<string name="yes">Да</string>
<string name="ok">ОК</string>
</resources>

View File

@ -1,5 +1,5 @@
<resources>
<string name="no_thanks">Hayır teşekkürler</string>
<string name="no_thanks">Hayır, teşekkürler</string>
<string name="yes">Evet</string>
<string name="ok">Tamam</string>
</resources>

View File

@ -0,0 +1,4 @@
<resources>
<string name="upgrade_msg">Надградете до целосната верзија на Magisk Manager за да го завршите поставувањето. Преземете и инсталирајте?</string>
<string name="no_internet_msg">Ве молиме поврзете се на интернет бидејќи е потребна надградба на целосната верзија на Magisk Manager.</string>
</resources>