Update SafetyNet

This commit is contained in:
topjohnwu 2019-01-30 17:11:03 -05:00
parent 3e9174deed
commit 454abc388b
6 changed files with 247 additions and 226 deletions

View File

@ -1,51 +0,0 @@
package com.topjohnwu.magisk.tasks;
import android.app.Activity;
import com.topjohnwu.magisk.App;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import dalvik.system.DexClassLoader;
public class SafetyNet {
public static final File EXT_APK =
new File(App.self.getFilesDir().getParent() + "/snet", "snet.apk");
private static void dyRun(Activity activity) 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,
(ISafetyNetHelper.Callback) code ->
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() < Const.SNET_EXT_VER)
throw new Exception();
helper.attest();
}
public static void check(Activity activity) {
try {
dyRun(activity);
} 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);
} catch (Exception e) {
e.printStackTrace();
Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
}
});
}
}
}

View File

@ -0,0 +1,158 @@
package com.topjohnwu.magisk.components;
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.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 dalvik.system.DexClassLoader;
public class SafetyNet implements ISafetyNetHelper.Callback {
private 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;
private ExpandableViewHolder expandable;
public SafetyNet(View v) {
new SafetyNet_ViewBinding(this, v);
expandable = new ExpandableViewHolder(expandLayout);
Context context = v.getContext();
safetyNetCard.setVisibility(hasGms(context) && Networking.checkNetworkStatus(context) ?
View.VISIBLE : View.GONE);
}
@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 (!SafetyNet.EXT_APK.exists()) {
// Show dialog
new CustomAlertDialog((Activity) 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();
}
}
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);
}
}
private void dyRun(Activity activity) 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, this);
if (helper.getVersion() < Const.SNET_EXT_VER)
throw new Exception();
helper.attest();
}
private void check(Activity activity) {
try {
dyRun(activity);
} 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);
} 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

@ -7,10 +7,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Config;
@ -18,16 +15,13 @@ import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseActivity; import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.BaseFragment; import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.components.EnvFixDialog; import com.topjohnwu.magisk.components.EnvFixDialog;
import com.topjohnwu.magisk.components.ExpandableViewHolder;
import com.topjohnwu.magisk.components.MagiskInstallDialog; import com.topjohnwu.magisk.components.MagiskInstallDialog;
import com.topjohnwu.magisk.components.ManagerInstallDialog; import com.topjohnwu.magisk.components.ManagerInstallDialog;
import com.topjohnwu.magisk.components.SafetyNet;
import com.topjohnwu.magisk.components.UninstallDialog; import com.topjohnwu.magisk.components.UninstallDialog;
import com.topjohnwu.magisk.components.UpdateCardHolder; import com.topjohnwu.magisk.components.UpdateCardHolder;
import com.topjohnwu.magisk.tasks.CheckUpdates; import com.topjohnwu.magisk.tasks.CheckUpdates;
import com.topjohnwu.magisk.tasks.SafetyNet;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.net.Networking; import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
@ -37,7 +31,6 @@ import java.util.Locale;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.cardview.widget.CardView; import androidx.cardview.widget.CardView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.transition.ChangeBounds; import androidx.transition.ChangeBounds;
@ -57,16 +50,6 @@ public class MagiskFragment extends BaseFragment
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.linearLayout) LinearLayout root; @BindView(R.id.linearLayout) LinearLayout root;
@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.expand_layout) LinearLayout 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;
@BindView(R.id.install_option_card) CardView installOptionCard; @BindView(R.id.install_option_card) CardView installOptionCard;
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox; @BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox; @BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
@ -80,32 +63,9 @@ public class MagiskFragment extends BaseFragment
private UpdateCardHolder magisk; private UpdateCardHolder magisk;
private UpdateCardHolder manager; private UpdateCardHolder manager;
private ExpandableViewHolder snExpandableHolder; private SafetyNet safetyNet;
private Transition transition; private Transition transition;
@OnClick(R.id.safetyNet_title)
void safetyNet() {
Runnable task = () -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetRefreshIcon.setVisibility(View.GONE);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
SafetyNet.check(requireActivity());
snExpandableHolder.collapse();
};
if (!SafetyNet.EXT_APK.exists()) {
// Show dialog
new CustomAlertDialog(requireActivity())
.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 magiskInstall(View v) { private void magiskInstall(View v) {
// Show Manager update first // Show Manager update first
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) { if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
@ -132,8 +92,7 @@ public class MagiskFragment extends BaseFragment
unbinder = new MagiskFragment_ViewBinding(this, v); unbinder = new MagiskFragment_ViewBinding(this, v);
requireActivity().setTitle(R.string.magisk); requireActivity().setTitle(R.string.magisk);
snExpandableHolder = new ExpandableViewHolder(expandLayout); safetyNet = new SafetyNet(v);
magisk = new UpdateCardHolder(inflater, root); magisk = new UpdateCardHolder(inflater, root);
manager = new UpdateCardHolder(inflater, root); manager = new UpdateCardHolder(inflater, root);
root.addView(magisk.itemView, 0); root.addView(magisk.itemView, 0);
@ -157,9 +116,6 @@ public class MagiskFragment extends BaseFragment
manager.additional.setVisibility(View.VISIBLE); manager.additional.setVisibility(View.VISIBLE);
} }
safetyNetCard.setVisibility(hasGms() && Networking.checkNetworkStatus(app) ?
View.VISIBLE : View.GONE);
transition = new TransitionSet() transition = new TransitionSet()
.setOrdering(TransitionSet.ORDERING_TOGETHER) .setOrdering(TransitionSet.ORDERING_TOGETHER)
.addTransition(new Fade(Fade.OUT)) .addTransition(new Fade(Fade.OUT))
@ -172,11 +128,9 @@ public class MagiskFragment extends BaseFragment
@Override @Override
public void onRefresh() { public void onRefresh() {
safetyNetStatusText.setText(R.string.safetyNet_check_text);
snExpandableHolder.setExpanded(false);
mSwipeRefreshLayout.setRefreshing(false); mSwipeRefreshLayout.setRefreshing(false);
TransitionManager.beginDelayedTransition(root, transition); TransitionManager.beginDelayedTransition(root, transition);
safetyNet.reset();
magisk.reset(); magisk.reset();
manager.reset(); manager.reset();
@ -197,19 +151,12 @@ public class MagiskFragment extends BaseFragment
@Override @Override
public int[] getSubscribedTopics() { public int[] getSubscribedTopics() {
return new int[] {Topic.SNET_CHECK_DONE, Topic.UPDATE_CHECK_DONE}; return new int[] {Topic.UPDATE_CHECK_DONE};
} }
@Override @Override
public void onPublish(int topic, Object[] result) { public void onPublish(int topic, Object[] result) {
switch (topic) { updateCheckUI();
case Topic.SNET_CHECK_DONE:
updateSafetyNetUI((int) result[0]);
break;
case Topic.UPDATE_CHECK_DONE:
updateCheckUI();
break;
}
} }
private boolean hasGms() { private boolean hasGms() {
@ -334,38 +281,5 @@ public class MagiskFragment extends BaseFragment
new EnvFixDialog(requireActivity()).show(); new EnvFixDialog(requireActivity()).show();
} }
} }
private void updateSafetyNetUI(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);
snExpandableHolder.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);
}
}
} }

View File

@ -1,9 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector android:height="24dp" android:tint="#FFFFFF"
android:width="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0"
android:height="24dp" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
android:viewportWidth="24.0" <path android:fillColor="#FF000000" android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z"/>
android:viewportHeight="24.0">
<path
android:fillColor="#fff"
android:pathData="M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zM10.8,10c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z"/>
</vector> </vector>

View File

@ -36,101 +36,105 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<RelativeLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/safetyNet_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content">
<ImageView <ImageView
android:id="@+id/safetyNet_refresh" android:id="@+id/sn_logo"
android:layout_width="25dp" android:layout_width="30dp"
android:layout_height="25dp" android:layout_height="30dp"
android:layout_centerVertical="true" android:layout_marginTop="12dp"
android:layout_margin="15dp" android:layout_marginEnd="24dp"
android:layout_toStartOf="@+id/safetyNet_status" android:layout_marginBottom="12dp"
app:srcCompat="@drawable/ic_refresh" /> android:tint="@color/green500"
app:layout_constraintBottom_toBottomOf="parent"
<ProgressBar app:layout_constraintEnd_toStartOf="@+id/safetyNet_status"
android:id="@+id/safetyNet_check_progress" app:layout_constraintTop_toTopOf="parent"
android:layout_width="25dp" app:srcCompat="@drawable/ic_safetynet" />
android:layout_height="25dp"
android:layout_centerVertical="true"
android:layout_margin="15dp"
android:layout_toStartOf="@+id/safetyNet_status"
android:visibility="gone" />
<TextView <TextView
android:id="@+id/safetyNet_status" android:id="@+id/safetyNet_status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center" android:gravity="center"
android:minWidth="175dp" android:minWidth="200dp"
android:padding="6dp"
android:text="@string/safetyNet_check_text" android:text="@string/safetyNet_check_text"
android:textStyle="bold" /> android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout> <ImageView
android:id="@+id/safetyNet_refresh"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginStart="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/safetyNet_status"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_refresh" />
<LinearLayout <ProgressBar
android:id="@+id/expand_layout" android:id="@+id/safetyNet_check_progress"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/safetyNet_refresh"
app:layout_constraintEnd_toEndOf="@+id/safetyNet_refresh"
app:layout_constraintStart_toStartOf="@+id/safetyNet_refresh"
app:layout_constraintTop_toTopOf="@+id/safetyNet_refresh" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/safetyNet_expand"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:paddingBottom="10dp">
android:paddingStart="10dp"
android:paddingEnd="10dp">
<LinearLayout <ImageView
android:layout_width="match_parent" android:id="@+id/cts_status_icon"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="@+id/cts_status"
app:layout_constraintEnd_toStartOf="@+id/cts_status"
app:layout_constraintTop_toTopOf="@+id/cts_status" />
<TextView
android:id="@+id/cts_status"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:padding="6dp"
android:orientation="horizontal"> android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/basic_status"
app:layout_constraintStart_toStartOf="@+id/basic_status"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/cts_status_icon" android:id="@+id/basic_status_icon"
android:layout_width="25dp" android:layout_width="25dp"
android:layout_height="25dp" android:layout_height="25dp"
android:layout_marginStart="10dp" android:layout_marginEnd="8dp"
android:layout_marginTop="5dp" app:layout_constraintBottom_toBottomOf="@+id/basic_status"
android:layout_marginEnd="10dp" app:layout_constraintEnd_toStartOf="@+id/basic_status"
android:layout_marginBottom="5dp" /> app:layout_constraintTop_toTopOf="@+id/basic_status" />
<TextView <TextView
android:id="@+id/cts_status" android:id="@+id/basic_status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minWidth="150dp"
android:padding="6dp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:padding="6dp"
android:orientation="horizontal"> android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cts_status" />
<ImageView
android:id="@+id/basic_status_icon"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_margin="10dp" />
<TextView </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/basic_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minWidth="150dp"
android:padding="6dp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -13,7 +13,7 @@
<string name="checking_for_updates">Checking for updates…</string> <string name="checking_for_updates">Checking for updates…</string>
<string name="magisk_update_available">Magisk v%1$s is available!</string> <string name="magisk_update_available">Magisk v%1$s is available!</string>
<string name="invalid_update_channel">Invalid Update Channel</string> <string name="invalid_update_channel">Invalid Update Channel</string>
<string name="safetyNet_check_text">Tap to start SafetyNet check.</string> <string name="safetyNet_check_text">Tap to start SafetyNet check</string>
<string name="checking_safetyNet_status">Checking SafetyNet status…</string> <string name="checking_safetyNet_status">Checking SafetyNet status…</string>
<string name="safetyNet_check_success">SafetyNet Check Success</string> <string name="safetyNet_check_success">SafetyNet Check Success</string>
<string name="safetyNet_api_error">SafetyNet API Error</string> <string name="safetyNet_api_error">SafetyNet API Error</string>