Commit 1b4ae70a authored by Viktor De Pasquale's avatar Viktor De Pasquale

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

parents 065051a3 b25c4972
......@@ -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:
......
......@@ -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}"
......
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()) }
}
package com.topjohnwu.magisk.model.entity.state
enum class IndeterminateState {
INDETERMINATE, CHECKED, UNCHECKED
}
\ No newline at end of file
CHECKED, INDETERMINATE, UNCHECKED
}
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 {
......
......@@ -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>() {
......
......@@ -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) }
......
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")
}
}
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"
}
}
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()
}
\ No newline at end of file
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;
}
}
......@@ -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);
}
......
......@@ -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
......
<?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" />
</LinearLayout>
<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>
# 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
......@@ -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>
......
This diff is collapsed.
......@@ -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>
......
......@@ -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>
......
......@@ -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
......
# 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)
......
This diff is collapsed.
......@@ -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);
......
<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>
<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>
<resources>
<string name="upgrade_msg">Надградете до целосната верзија на Magisk Manager за да го завршите поставувањето. Преземете и инсталирајте?</string>
<string name="no_internet_msg">Ве молиме поврзете се на интернет бидејќи е потребна надградба на целосната верзија на Magisk Manager.</string>
</resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment