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

Compare commits

..

24 Commits

Author SHA1 Message Date
topjohnwu
8d139e156e Adjust proguard settings to prevent crash 2018-01-12 03:33:50 +08:00
topjohnwu
7c2849356a Bump version 2018-01-12 01:57:31 +08:00
topjohnwu
0025ffd1c0 Update Trad. Chinese translation 2018-01-12 01:57:09 +08:00
topjohnwu
2ef7146642 Add fingerprint authentication 2018-01-12 01:53:49 +08:00
Grammatopoulos Apostolos
1b27e69e40 Greek translation updates 2018-01-11 21:04:29 +08:00
topjohnwu
8e7b757efd Fix dtbo detection 2018-01-10 20:41:55 +08:00
Michael Cerne
1ab543cea1 Minor language changes 2018-01-10 19:13:04 +08:00
Vv2233Bb
a3f86903e4 Lithuanian translation 2018-01-10 19:12:30 +08:00
Mevlüt TOPÇU
c239c305ab Update strings.xml 2018-01-10 19:04:26 +08:00
topjohnwu
2e02af994e Bump version 2018-01-02 00:25:08 +08:00
topjohnwu
836d9afe17 Update scripts 2018-01-01 16:46:08 +08:00
topjohnwu
007a352742 Update Trad. Chinese translations 2018-01-01 16:45:50 +08:00
vvb2060
e526e5659e Update zh-rCN translation 2018-01-01 16:39:15 +08:00
Rikka
4a5227c7bf Fix bug in SuDatabaseHelper 2018-01-01 01:11:45 +08:00
AndroPlus-org
c2c151ec4c Update Japanese translation 2018-01-01 01:09:56 +08:00
Jonas Schubert
452096e7e4 Added missing german translations 2018-01-01 01:09:21 +08:00
linar10
50c2a9859e Update strings.xml 2018-01-01 01:09:02 +08:00
Oliver Cervera
677b667307 Add sorting repo by update time
Add translation for new repo strings
2018-01-01 01:08:52 +08:00
topjohnwu
1adf331268 Bump version 2017-12-29 04:03:05 +08:00
topjohnwu
349b3e961b More robust sudb handling 2017-12-29 04:01:39 +08:00
topjohnwu
96650c06f0 Fix the issue that installation configs won't stick 2017-12-29 03:21:51 +08:00
dark-basic #DarkBasic BasicHD
26038a0a07 Update strings.xml 2017-12-29 01:44:36 +08:00
topjohnwu
6a148b5dd9 Add sorting repo by update time 2017-12-27 01:07:33 +08:00
topjohnwu
0e109ef979 Remove snet version checkpoint, always check by code 2017-12-26 18:24:43 +08:00
44 changed files with 965 additions and 415 deletions

View File

@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
android { android {
compileSdkVersion 27 compileSdkVersion 27
buildToolsVersion "27.0.2" buildToolsVersion "27.0.3"
defaultConfig { defaultConfig {
applicationId "com.topjohnwu.magisk" applicationId "com.topjohnwu.magisk"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 27
versionCode 82 versionCode 90
versionName "5.5.1" versionName "5.5.4"
ndk { ndk {
moduleName 'zipadjust' moduleName 'zipadjust'
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'

3
proguard-rules.pro vendored
View File

@@ -22,3 +22,6 @@
# BouncyCastle # BouncyCastle
-keep class org.bouncycastle.jcajce.provider.** { *; } -keep class org.bouncycastle.jcajce.provider.** { *; }
-dontwarn javax.naming.** -dontwarn javax.naming.**
# Gson
-keepattributes Signature

View File

@@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application <application
android:name=".MagiskManager" android:name=".MagiskManager"

View File

@@ -1,12 +1,3 @@
### v5.5.1 ### v5.5.4
- Fix an issue in setting up superuser database - Fix on-boot dtbo detection
- Add fingerprint authentication for Superuser requests
### v5.5.0
- Fix dynamic resource loading, prevent crashes when checking SafetyNet
- Update SignAPK to use very little RAM for supporting old devices
- Support settings migration after hiding Magisk Manager
- Add reboot menu in modules section
- Add dark theme to superuser request dialogs
- Properly handle new `HIGHCOMP` and add recommended `KEEPVERITY` and `KEEPFORCEENCRYPT` flags for installation
- Support new paths for v14.6
- Massive improvements in repackaging Magisk Manager

View File

@@ -91,7 +91,7 @@ public class MagiskFragment extends Fragment
new CheckSafetyNet(getActivity()).exec(); new CheckSafetyNet(getActivity()).exec();
collapse(); collapse();
}; };
if (mm.snetVersion < 0) { if (!CheckSafetyNet.dexPath.exists()) {
// Show dialog // Show dialog
new AlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())
.setTitle(R.string.proprietary_title) .setTitle(R.string.proprietary_title)
@@ -138,13 +138,9 @@ public class MagiskFragment extends Fragment
setupExpandable(); setupExpandable();
keepVerityChkbox.setChecked(mm.keepVerity); keepVerityChkbox.setChecked(mm.keepVerity);
keepVerityChkbox.setOnCheckedChangeListener((view, isChecked) -> { keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepVerity = checked);
mm.keepVerity = isChecked;
});
keepEncChkbox.setChecked(mm.keepEnc); keepEncChkbox.setChecked(mm.keepEnc);
keepEncChkbox.setOnCheckedChangeListener((view, isChecked) -> { keepEncChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepEnc = checked);
mm.keepEnc = isChecked;
});
mSwipeRefreshLayout.setOnRefreshListener(this); mSwipeRefreshLayout.setOnRefreshListener(this);
updateUI(); updateUI();

View File

@@ -49,7 +49,6 @@ public class MagiskManager extends Application {
public int remoteManagerVersionCode = -1; public int remoteManagerVersionCode = -1;
public String managerLink; public String managerLink;
public String bootBlock = null; public String bootBlock = null;
public int snetVersion;
public boolean keepVerity = false; public boolean keepVerity = false;
public boolean keepEnc = false; public boolean keepEnc = false;
@@ -63,8 +62,6 @@ public class MagiskManager extends Application {
public boolean magiskHide; public boolean magiskHide;
public boolean isDarkTheme; public boolean isDarkTheme;
public boolean updateNotification;
public boolean suReauth;
public int suRequestTimeout; public int suRequestTimeout;
public int suLogTimeout = 14; public int suLogTimeout = 14;
public int suAccessState; public int suAccessState;
@@ -75,7 +72,7 @@ public class MagiskManager extends Application {
public String localeConfig; public String localeConfig;
public int updateChannel; public int updateChannel;
public String bootFormat; public String bootFormat;
public String customChannelUrl; public int repoOrder;
// Global resources // Global resources
public SharedPreferences prefs; public SharedPreferences prefs;
@@ -106,7 +103,7 @@ public class MagiskManager extends Application {
} catch (PackageManager.NameNotFoundException ignored) { /* Expected */ } } catch (PackageManager.NameNotFoundException ignored) { /* Expected */ }
} }
suDB = new SuDatabaseHelper(false); suDB = SuDatabaseHelper.getSuDB(false);
repoDB = new RepoDatabaseHelper(this); repoDB = new RepoDatabaseHelper(this);
defaultLocale = Locale.getDefault(); defaultLocale = Locale.getDefault();
setLocale(); setLocale();
@@ -135,21 +132,38 @@ public class MagiskManager extends Application {
suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]); suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT); suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST); suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
suReauth = prefs.getBoolean(Const.Key.SU_REAUTH, false);
suAccessState = suDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB); suAccessState = suDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
multiuserMode = suDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY); multiuserMode = suDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
suNamespaceMode = suDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER); suNamespaceMode = suDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
// config // config
isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false); isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
updateNotification = prefs.getBoolean(Const.Key.UPDATE_NOTIFICATION, true);
updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL); updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img"); bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
snetVersion = prefs.getInt(Const.Key.SNET_VER, -1); repoOrder = prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_NAME);
customChannelUrl = prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
} }
public static void toast(String msg, int duration) { public void writeConfig() {
prefs.edit()
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
.putBoolean(Const.Key.HOSTS, Utils.itemExist(Const.MAGISK_HOST_FILE()))
.putBoolean(Const.Key.COREONLY, Utils.itemExist(Const.MAGISK_DISABLE_FILE))
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
.putString(Const.Key.LOCALE, localeConfig)
.putString(Const.Key.BOOT_FORMAT, bootFormat)
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
.putInt(Const.Key.REPO_ORDER, repoOrder)
.apply();
}
public static void toast(CharSequence msg, int duration) {
mHandler.post(() -> Toast.makeText(weakSelf.get(), msg, duration).show()); mHandler.post(() -> Toast.makeText(weakSelf.get(), msg, duration).show());
} }
@@ -190,6 +204,14 @@ public class MagiskManager extends Application {
if (Utils.isValidShellResponse(ret)) if (Utils.isValidShellResponse(ret))
bootBlock = ret.get(0); bootBlock = ret.get(0);
if (suDB != null && !SuDatabaseHelper.verified) {
suDB.close();
suDB = SuDatabaseHelper.getSuDB(true);
}
}
public void getDefaultInstallFlags() {
List<String> ret;
ret = Shell.su("echo \"$DTBOIMAGE\""); ret = Shell.su("echo \"$DTBOIMAGE\"");
if (Utils.isValidShellResponse(ret)) if (Utils.isValidShellResponse(ret))
keepVerity = true; keepVerity = true;
@@ -213,11 +235,6 @@ public class MagiskManager extends Application {
if (Utils.isValidShellResponse(ret)) if (Utils.isValidShellResponse(ret))
keepEnc = Boolean.parseBoolean(ret.get(0)); keepEnc = Boolean.parseBoolean(ret.get(0));
} catch (NumberFormatException ignored) {} } catch (NumberFormatException ignored) {}
if (suDB != null && !SuDatabaseHelper.verified) {
suDB.close();
suDB = new SuDatabaseHelper();
}
} }
public void setPermissionGrantCallback(Runnable callback) { public void setPermissionGrantCallback(Runnable callback) {

View File

@@ -1,5 +1,6 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
@@ -7,6 +8,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.SearchView; import android.widget.SearchView;
@@ -15,6 +17,7 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ReposAdapter; import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.asyncs.UpdateRepos; import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import butterknife.BindView; import butterknife.BindView;
@@ -98,6 +101,22 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
}); });
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
MagiskManager mm = getApplication();
if (item.getItemId() == R.id.repo_sort) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.sorting_order)
.setSingleChoiceItems(R.array.sorting_orders, mm.repoOrder, (d, which) -> {
mm.repoOrder = which;
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, mm.repoOrder).apply();
adapter.notifyDBChanged();
d.dismiss();
}).show();
}
return true;
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();

View File

@@ -22,6 +22,7 @@ import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.HideManager; import com.topjohnwu.magisk.asyncs.HideManager;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Const; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@@ -112,13 +113,14 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE); multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS); namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH); SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
updateChannel.setOnPreferenceChangeListener((pref, o) -> { updateChannel.setOnPreferenceChangeListener((pref, o) -> {
mm.updateChannel = Integer.parseInt((String) o); mm.updateChannel = Integer.parseInt((String) o);
if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) { if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) {
View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null); View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null);
EditText url = v.findViewById(R.id.custom_url); EditText url = v.findViewById(R.id.custom_url);
url.setText(mm.customChannelUrl); url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
new AlertDialog.Builder(getActivity()) new AlertDialog.Builder(getActivity())
.setTitle(R.string.settings_update_custom) .setTitle(R.string.settings_update_custom)
.setView(v) .setView(v)
@@ -144,6 +146,11 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
reauth.setSummary(R.string.android_o_not_support); reauth.setSummary(R.string.android_o_not_support);
} }
// Remove fingerprint option if not possible
if (!FingerprintHelper.canUseFingerprint()) {
suCategory.removePreference(fingerprint);
}
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME) && mm.magiskVersionCode >= 1440) { if (mm.getPackageName().equals(Const.ORIG_PKG_NAME) && mm.magiskVersionCode >= 1440) {
hideManager.setOnPreferenceClickListener((pref) -> { hideManager.setOnPreferenceClickListener((pref) -> {
Utils.runWithPermission(getActivity(), Utils.runWithPermission(getActivity(),

View File

@@ -34,6 +34,7 @@ public class SplashActivity extends Activity {
MagiskManager mm = getMagiskManager(); MagiskManager mm = getMagiskManager();
mm.loadMagiskInfo(); mm.loadMagiskInfo();
mm.getDefaultInstallFlags();
Utils.loadPrefs(); Utils.loadPrefs();
// Dynamic detect all locales // Dynamic detect all locales
@@ -61,7 +62,7 @@ public class SplashActivity extends Activity {
if (Shell.rootAccess() && mm.magiskVersionCode > 0) { if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
// Add update checking service // Add update checking service
if (Const.Value.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) { if (Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(this, UpdateCheckService.class); ComponentName service = new ComponentName(this, UpdateCheckService.class);
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service) JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
@@ -79,24 +80,7 @@ public class SplashActivity extends Activity {
} }
// Write back default values // Write back default values
mm.prefs.edit() mm.writeConfig();
.putBoolean(Const.Key.DARK_THEME, mm.isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, mm.magiskHide)
.putBoolean(Const.Key.UPDATE_NOTIFICATION, mm.updateNotification)
.putBoolean(Const.Key.HOSTS, Utils.itemExist(Const.MAGISK_HOST_FILE()))
.putBoolean(Const.Key.COREONLY, Utils.itemExist(Const.MAGISK_DISABLE_FILE))
.putBoolean(Const.Key.SU_REAUTH, mm.suReauth)
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(mm.suRequestTimeout))
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(mm.suResponseType))
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(mm.suNotificationType))
.putString(Const.Key.ROOT_ACCESS, String.valueOf(mm.suAccessState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(mm.multiuserMode))
.putString(Const.Key.SU_MNT_NS, String.valueOf(mm.suNamespaceMode))
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(mm.updateChannel))
.putString(Const.Key.LOCALE, mm.localeConfig)
.putString(Const.Key.BOOT_FORMAT, mm.bootFormat)
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.Value.UPDATE_SERVICE_VER)
.apply();
mm.hasInit = true; mm.hasInit = true;

View File

@@ -108,7 +108,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
@BindView(R.id.app_icon) ImageView appIcon; @BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appName; @BindView(R.id.app_name) TextView appName;
@BindView(R.id.app_package) TextView appPackage; @BindView(R.id.package_name) TextView appPackage;
@BindView(R.id.checkbox) CheckBox checkBox; @BindView(R.id.checkbox) CheckBox checkBox;
ViewHolder(View itemView) { ViewHolder(View itemView) {

View File

@@ -95,6 +95,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
String author = repo.getAuthor(); String author = repo.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author)); holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(repo.getDescription()); holder.description.setText(repo.getDescription());
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
holder.infoLayout.setOnClickListener(v -> holder.infoLayout.setOnClickListener(v ->
new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec()); new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec());
@@ -180,6 +181,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
@BindView(R.id.author) TextView author; @BindView(R.id.author) TextView author;
@BindView(R.id.info_layout) LinearLayout infoLayout; @BindView(R.id.info_layout) LinearLayout infoLayout;
@BindView(R.id.download) ImageView downloadImage; @BindView(R.id.download) ImageView downloadImage;
@BindView(R.id.update_time) TextView updateTime;
RepoHolder(View itemView) { RepoHolder(View itemView) {
super(itemView); super(itemView);

View File

@@ -22,13 +22,13 @@ import dalvik.system.DexClassLoader;
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> { public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
private File dexPath; public static final File dexPath =
new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
private DexClassLoader loader; private DexClassLoader loader;
private Class<?> helperClazz, callbackClazz; private Class<?> helperClazz, callbackClazz;
public CheckSafetyNet(Activity activity) { public CheckSafetyNet(Activity activity) {
super(activity); super(activity);
dexPath = new File(activity.getCacheDir().getParent() + "/snet", "snet.apk");
} }
private void dlSnet() throws IOException { private void dlSnet() throws IOException {
@@ -65,7 +65,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
e.printStackTrace(); e.printStackTrace();
} }
if (snet_ver != Const.Value.SNET_VER) { if (snet_ver != Const.SNET_VER) {
dlSnet(); dlSnet();
loadClasses(); loadClasses();
} }
@@ -79,8 +79,6 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
@Override @Override
protected void onPostExecute(Exception err) { protected void onPostExecute(Exception err) {
MagiskManager mm = MagiskManager.get(); MagiskManager mm = MagiskManager.get();
mm.snetVersion = Const.Value.SNET_VER;
mm.prefs.edit().putInt(Const.Key.SNET_VER, Const.Value.SNET_VER).apply();
try { try {
if (err != null) throw err; if (err != null) throw err;
Object helper = helperClazz.getConstructors()[0].newInstance( Object helper = helperClazz.getConstructors()[0].newInstance(

View File

@@ -33,7 +33,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
jsonStr = WebService.getString(Const.Url.BETA_URL); jsonStr = WebService.getString(Const.Url.BETA_URL);
break; break;
case Const.Value.CUSTOM_CHANNEL: case Const.Value.CUSTOM_CHANNEL:
jsonStr = WebService.getString(mm.customChannelUrl); jsonStr = WebService.getString(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
break; break;
} }
try { try {
@@ -54,7 +54,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
MagiskManager mm = MagiskManager.get(); MagiskManager mm = MagiskManager.get();
if (showNotification && mm.updateNotification) { if (showNotification && mm.prefs.getBoolean(Const.Key.UPDATE_NOTIFICATION, true)) {
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) { if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
ShowUI.managerUpdateNotification(); ShowUI.managerUpdateNotification();
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) { } else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {

View File

@@ -3,9 +3,11 @@ package com.topjohnwu.magisk.container;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Const; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
import java.text.DateFormat;
import java.util.Date; import java.util.Date;
public class Repo extends BaseModule { public class Repo extends BaseModule {
@@ -40,7 +42,7 @@ public class Repo extends BaseModule {
if (getVersionCode() < 0) { if (getVersionCode() < 0) {
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode"); throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
} }
if (getMinMagiskVersion() < Const.Value.MIN_MODULE_VER) { if (getMinMagiskVersion() < Const.MIN_MODULE_VER) {
throw new IllegalRepoException("Repo [" + repoName + "] is outdated"); throw new IllegalRepoException("Repo [" + repoName + "] is outdated");
} }
} }
@@ -78,6 +80,11 @@ public class Repo extends BaseModule {
return String.format(Const.Url.FILE_URL, repoName, "README.md"); return String.format(Const.Url.FILE_URL, repoName, "README.md");
} }
public String getLastUpdateString() {
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
MagiskManager.locale).format(mLastUpdate);
}
public Date getLastUpdate() { public Date getLastUpdate() {
return mLastUpdate; return mLastUpdate;
} }

View File

@@ -28,7 +28,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
// Clear bad repos // Clear bad repos
mDb.delete(TABLE_NAME, "minMagisk<?", mDb.delete(TABLE_NAME, "minMagisk<?",
new String[] { String.valueOf(Const.Value.MIN_MODULE_VER) }); new String[] { String.valueOf(Const.MIN_MODULE_VER) });
} }
@Override @Override
@@ -95,9 +95,17 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
} }
public Cursor getRepoCursor() { public Cursor getRepoCursor() {
String orderBy = null;
switch (mm.repoOrder) {
case Const.Value.ORDER_NAME:
orderBy = "name COLLATE NOCASE";
break;
case Const.Value.ORDER_DATE:
orderBy = "last_update DESC";
}
return mDb.query(TABLE_NAME, null, "minMagisk<=?", return mDb.query(TABLE_NAME, null, "minMagisk<=?",
new String[] { String.valueOf(mm.magiskVersionCode) }, new String[] { String.valueOf(mm.magiskVersionCode) },
null, null, "name COLLATE NOCASE"); null, null, orderBy);
} }
public List<String> getRepoIDList() { public List<String> getRepoIDList() {

View File

@@ -62,7 +62,7 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
File db = Utils.getDB(context, DB_NAME); File db = Utils.getDB(context, DB_NAME);
if (!verify) { if (!verify) {
if (db.length() == 0) { if (db.exists() && db.length() == 0) {
ce.loadMagiskInfo(); ce.loadMagiskInfo();
// Continue verification // Continue verification
} else { } else {
@@ -108,7 +108,7 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
// New global su db // New global su db
Shell.su(Utils.fmt("mkdir %s 2>/dev/null; chmod 700 %s", GLOBAL_DB.getParent(), GLOBAL_DB.getParent())); Shell.su(Utils.fmt("mkdir %s 2>/dev/null; chmod 700 %s", GLOBAL_DB.getParent(), GLOBAL_DB.getParent()));
if (!Utils.itemExist(GLOBAL_DB)) { if (!Utils.itemExist(GLOBAL_DB)) {
Utils.javaCreateFile(db); context.openOrCreateDatabase(DB_NAME, 0, null).close();
Shell.su(Utils.fmt("cp -af %s %s; rm -f %s*", db, GLOBAL_DB, db)); Shell.su(Utils.fmt("cp -af %s %s; rm -f %s*", db, GLOBAL_DB, db));
} }
verified = TextUtils.equals(Utils.checkInode(GLOBAL_DB), Utils.checkInode(db)); verified = TextUtils.equals(Utils.checkInode(GLOBAL_DB), Utils.checkInode(db));
@@ -125,12 +125,17 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
return context; return context;
} }
public SuDatabaseHelper() { public static SuDatabaseHelper getSuDB(boolean verify) {
this(true); try {
} return new SuDatabaseHelper(initDB(verify));
} catch(Exception e) {
public SuDatabaseHelper(boolean verify) { // Try to catch runtime exceptions and remove all db for retry
this(initDB(verify)); unmntDB();
Shell.su(Utils.fmt("rm -rf /data/user*/*/magisk.db /data/adb/magisk.db /data/user*/*/%s/databases",
MagiskManager.get().getPackageName()));
e.printStackTrace();
return new SuDatabaseHelper(initDB(false));
}
} }
private SuDatabaseHelper(Context context) { private SuDatabaseHelper(Context context) {

View File

@@ -5,6 +5,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@@ -18,7 +19,7 @@ public class PackageReceiver extends BroadcastReceiver {
switch (intent.getAction()) { switch (intent.getAction()) {
case Intent.ACTION_PACKAGE_REPLACED: case Intent.ACTION_PACKAGE_REPLACED:
// This will only work pre-O // This will only work pre-O
if (mm.suReauth) { if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
mm.suDB.deletePolicy(pkg); mm.suDB.deletePolicy(pkg);
} }
break; break;

View File

@@ -41,6 +41,7 @@ public class OnBootIntentService extends IntentService {
* */ * */
MagiskManager mm = Utils.getMagiskManager(this); MagiskManager mm = Utils.getMagiskManager(this);
mm.loadMagiskInfo(); mm.loadMagiskInfo();
mm.getDefaultInstallFlags();
if (Shell.rootAccess()) { if (Shell.rootAccess()) {
Utils.patchDTBO(); Utils.patchDTBO();
} }

View File

@@ -3,12 +3,14 @@ package com.topjohnwu.magisk.superuser;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.LocalSocket; import android.net.LocalSocket;
import android.net.LocalSocketAddress; import android.net.LocalSocketAddress;
import android.os.Bundle; import android.os.Bundle;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.FileObserver; import android.os.FileObserver;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View;
import android.view.Window; import android.view.Window;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
@@ -23,6 +25,7 @@ import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.container.Policy; import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.Const; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import java.io.DataInputStream; import java.io.DataInputStream;
@@ -40,6 +43,8 @@ public class RequestActivity extends Activity {
@BindView(R.id.package_name) TextView packageNameView; @BindView(R.id.package_name) TextView packageNameView;
@BindView(R.id.grant_btn) Button grant_btn; @BindView(R.id.grant_btn) Button grant_btn;
@BindView(R.id.deny_btn) Button deny_btn; @BindView(R.id.deny_btn) Button deny_btn;
@BindView(R.id.fingerprint) ImageView fingerprintImg;
@BindView(R.id.warning) TextView warning;
private String socketPath; private String socketPath;
private LocalSocket socket; private LocalSocket socket;
@@ -49,6 +54,7 @@ public class RequestActivity extends Activity {
private boolean hasTimeout; private boolean hasTimeout;
private Policy policy; private Policy policy;
private CountDownTimer timer; private CountDownTimer timer;
private FingerprintHelper fingerprintHelper;
@Override @Override
public int getDarkTheme() { public int getDarkTheme() {
@@ -80,6 +86,15 @@ public class RequestActivity extends Activity {
new SocketManager(this).exec(); new SocketManager(this).exec();
} }
@Override
public void finish() {
if (timer != null)
timer.cancel();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
super.finish();
}
private boolean cancelTimeout() { private boolean cancelTimeout() {
timer.cancel(); timer.cancel();
deny_btn.setText(getString(R.string.deny)); deny_btn.setText(getString(R.string.deny));
@@ -128,11 +143,49 @@ public class RequestActivity extends Activity {
} }
}; };
grant_btn.setOnClickListener(v -> { boolean useFingerprint = mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) && FingerprintHelper.canUseFingerprint();
handleAction(Policy.ALLOW);
timer.cancel(); if (useFingerprint) {
}); try {
grant_btn.requestFocus(); fingerprintHelper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
warning.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
warning.setText(helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
handleAction(Policy.ALLOW);
}
@Override
public void onAuthenticationFailed() {
warning.setText(R.string.auth_fail);
}
};
fingerprintHelper.startAuth();
} catch (Exception e) {
e.printStackTrace();
useFingerprint = false;
}
}
if (!useFingerprint) {
grant_btn.setOnClickListener(v -> {
handleAction(Policy.ALLOW);
timer.cancel();
});
grant_btn.requestFocus();
}
grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE);
fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE);
deny_btn.setOnClickListener(v -> { deny_btn.setOnClickListener(v -> {
handleAction(Policy.DENY); handleAction(Policy.DENY);
timer.cancel(); timer.cancel();
@@ -151,8 +204,9 @@ public class RequestActivity extends Activity {
public void onBackPressed() { public void onBackPressed() {
if (policy != null) { if (policy != null) {
handleAction(Policy.DENY); handleAction(Policy.DENY);
} else {
finish();
} }
finish();
} }
void handleAction() { void handleAction() {

View File

@@ -23,6 +23,8 @@ public class Const {
public static final String UTIL_FUNCTIONS= "util_functions.sh"; public static final String UTIL_FUNCTIONS= "util_functions.sh";
public static final String ANDROID_MANIFEST = "AndroidManifest.xml"; public static final String ANDROID_MANIFEST = "AndroidManifest.xml";
public static final String SU_KEYSTORE_KEY = "su_key";
// Paths // Paths
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk"; public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
public static final String TMP_FOLDER_PATH = "/dev/tmp"; public static final String TMP_FOLDER_PATH = "/dev/tmp";
@@ -30,6 +32,11 @@ public class Const {
public static final File EXTERNAL_PATH = new File(Environment.getExternalStorageDirectory(), "MagiskManager"); public static final File EXTERNAL_PATH = new File(Environment.getExternalStorageDirectory(), "MagiskManager");
public static final String MANAGER_CONFIGS = ".tmp.magisk.config"; public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
// Versions
public static final int UPDATE_SERVICE_VER = 1;
public static final int SNET_VER = 7;
public static final int MIN_MODULE_VER = 1400;
public static String BUSYBOX_PATH() { public static String BUSYBOX_PATH() {
if (Utils.itemExist("/sbin/.core/busybox/busybox")) { if (Utils.itemExist("/sbin/.core/busybox/busybox")) {
return "/sbin/.core/busybox"; return "/sbin/.core/busybox";
@@ -104,6 +111,7 @@ public class Const {
public static final String SU_AUTO_RESPONSE = "su_auto_response"; public static final String SU_AUTO_RESPONSE = "su_auto_response";
public static final String SU_NOTIFICATION = "su_notification"; public static final String SU_NOTIFICATION = "su_notification";
public static final String SU_REAUTH = "su_reauth"; public static final String SU_REAUTH = "su_reauth";
public static final String SU_FINGERPRINT = "su_fingerprint";
// intents // intents
public static final String OPEN_SECTION = "section"; public static final String OPEN_SECTION = "section";
@@ -118,7 +126,6 @@ public class Const {
public static final String UPDATE_CHANNEL = "update_channel"; public static final String UPDATE_CHANNEL = "update_channel";
public static final String CUSTOM_CHANNEL = "custom_channel"; public static final String CUSTOM_CHANNEL = "custom_channel";
public static final String BOOT_FORMAT = "boot_format"; public static final String BOOT_FORMAT = "boot_format";
public static final String SNET_VER = "snet_version";
public static final String UPDATE_SERVICE_VER = "update_service_version"; public static final String UPDATE_SERVICE_VER = "update_service_version";
public static final String APP_VER = "app_version"; public static final String APP_VER = "app_version";
public static final String MAGISKHIDE = "magiskhide"; public static final String MAGISKHIDE = "magiskhide";
@@ -129,6 +136,7 @@ public class Const {
public static final String ETAG_KEY = "ETag"; public static final String ETAG_KEY = "ETag";
public static final String LINK_KEY = "Link"; public static final String LINK_KEY = "Link";
public static final String IF_NONE_MATCH = "If-None-Match"; public static final String IF_NONE_MATCH = "If-None-Match";
public static final String REPO_ORDER = "repo_order";
} }
@@ -158,8 +166,7 @@ public class Const {
public static final String PATCH_BOOT = "patch"; public static final String PATCH_BOOT = "patch";
public static final String FLASH_MAGISK = "magisk"; public static final String FLASH_MAGISK = "magisk";
public static final int[] timeoutList = {0, -1, 10, 20, 30, 60}; public static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
public static final int UPDATE_SERVICE_VER = 1; public static final int ORDER_NAME = 0;
public static final int SNET_VER = 7; public static final int ORDER_DATE = 1;
public static final int MIN_MODULE_VER = 1400;
} }
} }

View File

@@ -0,0 +1,84 @@
package com.topjohnwu.magisk.utils;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import com.topjohnwu.magisk.MagiskManager;
import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@TargetApi(Build.VERSION_CODES.M)
public abstract class FingerprintHelper extends FingerprintManager.AuthenticationCallback {
private FingerprintManager manager;
private Cipher cipher;
private CancellationSignal cancel;
public static boolean canUseFingerprint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return false;
MagiskManager mm = MagiskManager.get();
KeyguardManager km = mm.getSystemService(KeyguardManager.class);
FingerprintManager fm = mm.getSystemService(FingerprintManager.class);
return km.isKeyguardSecure() && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
}
protected FingerprintHelper() throws Exception {
MagiskManager mm = MagiskManager.get();
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = mm.getSystemService(FingerprintManager.class);
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(Const.SU_KEYSTORE_KEY, null);
if (key == null) {
key = generateKey();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
} catch (KeyPermanentlyInvalidatedException e) {
// Only happens on Marshmallow
key = generateKey();
cipher.init(Cipher.ENCRYPT_MODE, key);
}
}
public void startAuth() {
cancel = new CancellationSignal();
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
manager.authenticate(cryptoObject, cancel, 0, this, null);
}
public void cancel() {
if (cancel != null)
cancel.cancel();
}
private SecretKey generateKey() throws Exception {
KeyGenerator keygen = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
Const.SU_KEYSTORE_KEY,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setInvalidatedByBiometricEnrollment(false);
}
keygen.init(builder.build());
return keygen.generateKey();
}
}

View File

@@ -89,13 +89,10 @@ public class Shell {
} }
// Root shell initialization // Root shell initialization
String bbpath = Const.BUSYBOX_PATH();
mm.shell.run_raw(false, false, mm.shell.run_raw(false, false,
"export PATH=" + bbpath + ":$PATH", "export PATH=" + Const.BUSYBOX_PATH() + ":$PATH",
"mount_partitions", "mount_partitions",
"find_boot_image", "run_migrations");
"find_dtbo_image",
"migrate_boot_backup");
} }
} }

View File

@@ -11,8 +11,8 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.jar.JarEntry; import java.util.zip.ZipEntry;
import java.util.jar.JarInputStream; import java.util.zip.ZipInputStream;
public class ZipUtils { public class ZipUtils {
@@ -30,9 +30,9 @@ public class ZipUtils {
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws Exception { public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws Exception {
try { try {
JarInputStream zipfile = new JarInputStream(zip); ZipInputStream zipfile = new ZipInputStream(zip);
JarEntry entry; ZipEntry entry;
while ((entry = zipfile.getNextJarEntry()) != null) { while ((entry = zipfile.getNextEntry()) != null) {
if (!entry.getName().startsWith(path) || entry.isDirectory()){ if (!entry.getName().startsWith(path) || entry.isDirectory()){
// Ignore directories, only create files // Ignore directories, only create files
continue; continue;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/imageColorTint"
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
</vector>

View File

@@ -101,18 +101,27 @@
<Button <Button
style="?android:buttonBarButtonStyle" style="?android:buttonBarButtonStyle"
android:text="@string/deny_with_str" android:text="@string/deny_with_str"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/deny_btn" android:id="@+id/deny_btn"
android:layout_weight="1" /> android:layout_weight="1" />
<Button <Button
style="?android:buttonBarButtonStyle"
android:text="@string/grant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/grant_btn" android:id="@+id/grant_btn"
android:layout_weight="1" /> style="?android:buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/grant" />
<ImageView
android:id="@+id/fingerprint"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="7dp"
android:tint="?attr/colorAccent"
android:src="@drawable/ic_fingerprint" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -15,71 +15,63 @@
card_view:cardCornerRadius="@dimen/card_corner_radius" card_view:cardCornerRadius="@dimen/card_corner_radius"
card_view:cardElevation="@dimen/card_elevation"> card_view:cardElevation="@dimen/card_elevation">
<RelativeLayout <LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="fill_parent" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:id="@+id/info_layout"
android:padding="@dimen/card_layout_padding"> android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<ImageView <ImageView
android:id="@+id/app_icon" android:id="@+id/app_icon"
android:layout_width="@dimen/card_appicon_size" android:layout_width="50dp"
android:layout_height="@dimen/card_appicon_size" android:layout_height="50dp"
android:layout_centerVertical="true" android:layout_gravity="center_vertical"
android:scaleType="centerCrop"/> android:gravity="end" />
<LinearLayout <LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignBottom="@+id/app_icon"
android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:paddingEnd="@dimen/card_appicon_size" android:layout_width="0dp"
android:paddingStart="65dp" android:layout_height="wrap_content"
android:weightSum="1"> android:layout_weight="1"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp">
<TextView <TextView
android:id="@+id/app_name" android:id="@+id/app_name"
android:layout_width="fill_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1" android:maxLines="1"
android:paddingEnd="3dp" android:ellipsize="end"
android:paddingStart="3dp" android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"/> android:textIsSelectable="false"/>
<TextView <TextView
android:id="@+id/app_package" android:id="@+id/package_name"
android:layout_width="fill_parent" android:layout_width="match_parent"
android:layout_height="25dp" android:layout_height="wrap_content"
android:ellipsize="marquee" android:maxLines="1"
android:gravity="center_vertical" android:ellipsize="end"
android:marqueeRepeatLimit="marquee_forever" android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingEnd="3dp" android:textColor="@android:color/tertiary_text_dark"
android:paddingStart="3dp" android:textIsSelectable="false" />
android:singleLine="true"/>
</LinearLayout> </LinearLayout>
<LinearLayout <CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:focusable="false"
android:layout_centerVertical="true"> android:layout_gravity="center_vertical"
android:src="@drawable/ic_menu_overflow_material"
<CheckBox android:checked="false" />
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:gravity="center"
android:src="@drawable/ic_menu_overflow_material"
android:checked="false" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView> </android.support.v7.widget.CardView>

View File

@@ -52,7 +52,6 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:textIsSelectable="false"/> android:textIsSelectable="false"/>
<TextView <TextView

View File

@@ -67,6 +67,15 @@
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textIsSelectable="false" /> android:textIsSelectable="false" />
<TextView
android:id="@+id/update_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic" />
</LinearLayout> </LinearLayout>
<ImageView <ImageView

View File

@@ -2,9 +2,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/repo_sort"
android:icon="@drawable/ic_sort"
app:showAsAction="always"/>
<item <item
android:id="@+id/repo_search" android:id="@+id/repo_search"
android:title="" android:title=""
app:actionViewClass="android.widget.SearchView" app:actionViewClass="android.widget.SearchView"
app:showAsAction="always"/> app:showAsAction="always"/>
</menu> </menu>

View File

@@ -53,6 +53,10 @@
<string name="update_available">Aktualisierung verfügbar</string> <string name="update_available">Aktualisierung verfügbar</string>
<string name="installed">Installiert</string> <string name="installed">Installiert</string>
<string name="not_installed">Nicht installiert</string> <string name="not_installed">Nicht installiert</string>
<string name="updated_on">Zuletzt aktualisiert: %1$s</string>
<string name="sorting_order">Sortierung</string>
<string name="sort_by_name">Nach Namen sortiert</string>
<string name="sort_by_update">Nach Aktualisierung sortiert</string>
<!--Log Fragment--> <!--Log Fragment-->
<string name="menuSaveLog">Protokoll speichern</string> <string name="menuSaveLog">Protokoll speichern</string>
@@ -179,6 +183,7 @@
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namensraum</string> <string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namensraum</string>
<string name="requester_summary">Root-Sitzungen erben den Namensraum des Abfragenden</string> <string name="requester_summary">Root-Sitzungen erben den Namensraum des Abfragenden</string>
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namensraum</string> <string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namensraum</string>
<string name="android_o_not_support">Android 8.0+ wird nicht unterstützt</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser-Anfrage</string> <string name="su_request_title">Superuser-Anfrage</string>

File diff suppressed because it is too large Load Diff

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