Separate backend logic from frontend UI

This commit is contained in:
topjohnwu 2018-12-13 04:35:50 -05:00
parent 006d28abd5
commit ff3d66a661
87 changed files with 1194 additions and 1138 deletions

1
app/.gitignore vendored
View File

@ -6,7 +6,6 @@
app/release
*.hprof
.externalNativeBuild/
src/full/res/raw/util_functions.sh
public.certificate.x509.pem
private.key.pk8
*.apk

View File

@ -73,6 +73,7 @@ dependencies {
implementation 'androidx.core:core:1.0.1'
implementation project(':net')
fullImplementation project(':utils')
fullImplementation project(':core')
fullImplementation 'androidx.appcompat:appcompat:1.0.2'
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"

View File

@ -23,14 +23,11 @@
-dontwarn javax.naming.**
# Snet extention
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
-keepclassmembers class com.topjohnwu.magisk.utils.BootSigner { *; }
# Fast Android Networking Library
-dontwarn okhttp3.**
-keepclassmembers class com.topjohnwu.core.utils.ISafetyNetHelper { *; }
-keepclassmembers class com.topjohnwu.core.utils.BootSigner { *; }
# Strip logging
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
-assumenosideeffects class com.topjohnwu.core.utils.Logger {
public *** debug(...);
}

View File

@ -40,9 +40,6 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name="a.g"
android:theme="@style/AppTheme.Translucent" />
<!-- Superuser -->

View File

@ -1,6 +1,6 @@
package a;
import com.topjohnwu.magisk.utils.BootSigner;
import com.topjohnwu.core.utils.BootSigner;
import androidx.annotation.Keep;

View File

@ -1,7 +0,0 @@
package a;
import com.topjohnwu.magisk.NoUIActivity;
public class g extends NoUIActivity {
/* stub */
}

View File

@ -1,7 +1,7 @@
package a;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
public class q extends MagiskManager {
public class q extends App {
/* stub */
}

View File

@ -5,10 +5,11 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.core.Const;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.components.MarkDownWindow;
import com.topjohnwu.magisk.utils.AppUtils;
import java.util.Locale;
@ -62,9 +63,9 @@ public class AboutActivity extends BaseActivity {
appTranslators.setSummary(translators);
}
appSourceCode.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.SOURCE_CODE_URL)));
supportThread.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.XDA_THREAD)));
twitter.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.TWITTER_URL)));
appSourceCode.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.SOURCE_CODE_URL)));
supportThread.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.XDA_THREAD)));
twitter.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.TWITTER_URL)));
setFloating();
}

View File

@ -0,0 +1,34 @@
package com.topjohnwu.magisk;
import com.topjohnwu.core.App;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.services.UpdateCheckService;
import java.util.HashMap;
import java.util.Map;
public class ClassMap {
private static Map<Class, Class> classMap = new HashMap<>();
static {
classMap.put(App.class, a.q.class);
classMap.put(MainActivity.class, a.b.class);
classMap.put(SplashActivity.class, a.c.class);
classMap.put(AboutActivity.class, a.d.class);
classMap.put(DonationActivity.class, a.e.class);
classMap.put(FlashActivity.class, a.f.class);
classMap.put(GeneralReceiver.class, a.h.class);
classMap.put(ShortcutReceiver.class, a.i.class);
classMap.put(OnBootService.class, a.j.class);
classMap.put(UpdateCheckService.class, a.k.class);
classMap.put(AboutCardRow.class, a.l.class);
classMap.put(SuRequestActivity.class, a.m.class);
}
public static Class get(Class c) {
return classMap.get(c);
}
}

View File

@ -3,9 +3,10 @@ package com.topjohnwu.magisk;
import android.net.Uri;
import android.os.Bundle;
import com.topjohnwu.core.Const;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.AppUtils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@ -38,7 +39,7 @@ public class DonationActivity extends BaseActivity {
ab.setDisplayHomeAsUpEnabled(true);
}
paypal.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.PAYPAL_URL)));
patreon.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.PATREON_URL)));
paypal.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.PAYPAL_URL)));
patreon.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.PATREON_URL)));
}
}

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -12,18 +11,22 @@ import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.tasks.FlashZip;
import com.topjohnwu.core.tasks.MagiskInstaller;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@ -40,7 +43,7 @@ public class FlashActivity extends BaseActivity {
@BindView(R.id.reboot) public Button reboot;
@BindView(R.id.scrollView) ScrollView sv;
private List<String> logs;
private List<String> console, logs;
@OnClick(R.id.reboot)
void reboot() {
@ -49,7 +52,7 @@ public class FlashActivity extends BaseActivity {
@OnClick(R.id.save_logs)
void saveLogs() {
runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
runWithExternalRW(() -> {
Calendar now = Calendar.getInstance();
String filename = String.format(Locale.US,
"magisk_install_log_%04d%02d%02d_%02d%02d%02d.log",
@ -71,6 +74,17 @@ public class FlashActivity extends BaseActivity {
});
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button
}
@Override
public int getDarkTheme() {
return R.style.AppTheme_StatusBar_Dark;
@ -92,8 +106,36 @@ public class FlashActivity extends BaseActivity {
if (!Shell.rootAccess())
reboot.setVisibility(View.GONE);
logs = new ArrayList<>();
CallbackList<String> console = new CallbackList<String>(new ArrayList<>()) {
logs = Collections.synchronizedList(new ArrayList<>());
console = new ConsoleList();
Intent intent = getIntent();
Uri uri = intent.getData();
switch (intent.getStringExtra(Const.Key.FLASH_ACTION)) {
case Const.Value.FLASH_ZIP:
new FlashModule(uri).exec();
break;
case Const.Value.UNINSTALL:
new Uninstall(uri).exec();
break;
case Const.Value.FLASH_MAGISK:
new DirectInstall().exec();
break;
case Const.Value.FLASH_INACTIVE_SLOT:
new SecondSlot().exec();
break;
case Const.Value.PATCH_BOOT:
new PatchBoot(uri).exec();
break;
}
}
private class ConsoleList extends CallbackList<String> {
ConsoleList() {
super(new ArrayList<>());
}
private void updateUI() {
flashLogs.setText(TextUtils.join("\n", this));
@ -109,60 +151,110 @@ public class FlashActivity extends BaseActivity {
@Override
public String set(int i, String s) {
String ret = super.set(i, s);
Data.mainHandler.post(this::updateUI);
App.mainHandler.post(this::updateUI);
return ret;
}
};
// We must receive a Uri of the target zip
Intent intent = getIntent();
Uri uri = intent.getData();
switch (intent.getStringExtra(Const.Key.FLASH_ACTION)) {
case Const.Value.FLASH_ZIP:
new FlashZip(this, uri, console, logs).exec();
break;
case Const.Value.UNINSTALL:
new UninstallMagisk(this, uri, console, logs).exec();
break;
case Const.Value.FLASH_MAGISK:
new InstallMagisk(this, console, logs, InstallMagisk.DIRECT_MODE).exec();
break;
case Const.Value.FLASH_INACTIVE_SLOT:
new InstallMagisk(this, console, logs, InstallMagisk.SECOND_SLOT_MODE).exec();
break;
case Const.Value.PATCH_BOOT:
new InstallMagisk(this, console, logs,
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
break;
}
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
private class FlashModule extends FlashZip {
FlashModule(Uri uri) {
super(uri, console, logs);
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button
}
private static class UninstallMagisk extends FlashZip {
private UninstallMagisk(BaseActivity context, Uri uri, List<String> console, List<String> logs) {
super(context, uri, console, logs);
}
@Override
protected void onPostExecute(Integer result) {
if (result == 1) {
Data.mainHandler.postDelayed(() ->
Shell.su("pm uninstall " + getActivity().getPackageName()).exec(), 3000);
protected void onResult(boolean success) {
if (success) {
Utils.loadModules();
} else {
super.onPostExecute(result);
console.add("! Installation failed");
reboot.setVisibility(View.GONE);
}
buttonPanel.setVisibility(View.VISIBLE);
}
}
private class Uninstall extends FlashModule {
Uninstall(Uri uri) {
super(uri);
}
@Override
protected void onResult(boolean success) {
if (success)
App.mainHandler.postDelayed(Shell.su("pm uninstall " + getPackageName())::exec, 3000);
else
super.onResult(false);
}
}
private abstract class BaseInstaller extends MagiskInstaller {
BaseInstaller() {
super(console, logs);
}
@Override
protected void onResult(boolean success) {
if (success) {
console.add("- All done!");
} else {
Shell.sh("rm -rf " + installDir).submit();
console.add("! Installation failed");
reboot.setVisibility(View.GONE);
}
buttonPanel.setVisibility(View.VISIBLE);
}
}
private class DirectInstall extends BaseInstaller {
@Override
protected boolean operations() {
console.add("- Detecting target image");
srcBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
if (srcBoot.isEmpty()) {
console.add("! Unable to detect target image");
return false;
}
return extractZip() && patchBoot() && flashBoot();
}
}
private class SecondSlot extends BaseInstaller {
@Override
protected boolean operations() {
String slot = ShellUtils.fastCmd("echo $SLOT");
String target = (TextUtils.equals(slot, "_a") ? "_b" : "_a");
console.add("- Target slot: " + target);
console.add("- Detecting target image");
srcBoot = ShellUtils.fastCmd(
"SLOT=" + target,
"find_boot_image",
"SLOT=" + slot,
"echo \"$BOOTIMAGE\""
);
if (srcBoot.isEmpty()) {
console.add("! Unable to detect target image");
return false;
}
return extractZip() && patchBoot() && flashBoot() && postOTA();
}
}
private class PatchBoot extends BaseInstaller {
private Uri uri;
PatchBoot(Uri u) {
uri = u;
}
@Override
protected boolean operations() {
return copyBoot(uri) && extractZip() && patchBoot() && storeBoot();
}
}
}

View File

@ -8,6 +8,10 @@ import android.view.MenuItem;
import android.view.View;
import com.google.android.material.navigation.NavigationView;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.fragments.LogFragment;
import com.topjohnwu.magisk.fragments.MagiskFragment;
@ -17,8 +21,6 @@ import com.topjohnwu.magisk.fragments.ReposFragment;
import com.topjohnwu.magisk.fragments.SettingsFragment;
import com.topjohnwu.magisk.fragments.SuperuserFragment;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import androidx.annotation.NonNull;
@ -49,8 +51,8 @@ public class MainActivity extends BaseActivity
@Override
protected void onCreate(final Bundle savedInstanceState) {
if (!mm.hasInit) {
startActivity(new Intent(this, Data.classMap.get(SplashActivity.class)));
if (!app.init) {
startActivity(new Intent(this, ClassMap.get(SplashActivity.class)));
finish();
}
@ -120,7 +122,7 @@ public class MainActivity extends BaseActivity
public void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
app.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
&& Shell.rootAccess() && Data.magiskVersionCode >= 0);
@ -189,11 +191,11 @@ public class MainActivity extends BaseActivity
displayFragment(new SettingsFragment(), true);
break;
case R.id.app_about:
startActivity(new Intent(this, Data.classMap.get(AboutActivity.class)));
startActivity(new Intent(this, ClassMap.get(AboutActivity.class)));
mDrawerItem = bak;
break;
case R.id.donation:
startActivity(new Intent(this, Data.classMap.get(DonationActivity.class)));
startActivity(new Intent(this, ClassMap.get(DonationActivity.class)));
mDrawerItem = bak;
break;
}

View File

@ -1,13 +0,0 @@
package com.topjohnwu.magisk;
import com.topjohnwu.magisk.components.BaseActivity;
import androidx.annotation.NonNull;
public class NoUIActivity extends BaseActivity {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
finish();
}
}

View File

@ -5,14 +5,17 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.core.tasks.UpdateRepos;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.utils.AppUtils;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
public class SplashActivity extends BaseActivity {
@ -21,9 +24,9 @@ public class SplashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String pkg = mm.mDB.getStrings(Const.Key.SU_MANAGER, null);
String pkg = app.mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(BuildConfig.APPLICATION_ID)) {
mm.mDB.setStrings(Const.Key.SU_MANAGER, null);
app.mDB.setStrings(Const.Key.SU_MANAGER, null);
Shell.su("pm uninstall " + pkg).exec();
}
if (TextUtils.equals(pkg, getPackageName())) {
@ -37,7 +40,7 @@ public class SplashActivity extends BaseActivity {
// Magisk working as expected
if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
// Update check service
Utils.setupUpdateCheck();
AppUtils.setupUpdateCheck();
// Load modules
Utils.loadModules();
}
@ -45,13 +48,13 @@ public class SplashActivity extends BaseActivity {
Data.importPrefs();
// Dynamic detect all locales
LocaleManager.loadAvailableLocales();
LocaleManager.loadAvailableLocales(R.string.download_file_error);
// Create notification channel on Android O
Notifications.setup(this);
// Setup shortcuts
sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
sendBroadcast(new Intent(this, ClassMap.get(ShortcutReceiver.class)));
if (Download.checkNetworkStatus(this)) {
// Fire update check
@ -63,9 +66,9 @@ public class SplashActivity extends BaseActivity {
// Write back default values
Data.writeConfig();
mm.hasInit = true;
app.init = true;
Intent intent = new Intent(this, Data.classMap.get(MainActivity.class));
Intent intent = new Intent(this, ClassMap.get(MainActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
startActivity(intent);

View File

@ -15,8 +15,10 @@ import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.SuConnector;
@ -70,7 +72,7 @@ public class SuRequestActivity extends BaseActivity {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
PackageManager pm = getPackageManager();
mm.mDB.clearOutdated();
app.mDB.clearOutdated();
// Get policy
Intent intent = getIntent();
@ -84,7 +86,7 @@ public class SuRequestActivity extends BaseActivity {
};
Bundle bundle = connector.readSocketInput();
int uid = Integer.parseInt(bundle.getString("uid"));
policy = mm.mDB.getPolicy(uid);
policy = app.mDB.getPolicy(uid);
if (policy == null) {
policy = new Policy(uid, pm);
}
@ -212,7 +214,7 @@ public class SuRequestActivity extends BaseActivity {
policy.policy = action;
if (time >= 0) {
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
mm.mDB.updatePolicy(policy);
app.mDB.updatePolicy(policy);
}
handleAction();
}

View File

@ -13,10 +13,10 @@ import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;

View File

@ -10,9 +10,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.superuser.Shell;
import java.util.List;

View File

@ -10,12 +10,12 @@ import android.widget.Switch;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.core.database.MagiskDB;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.MagiskDB;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.util.HashSet;

View File

@ -11,14 +11,14 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.container.Repo;
import com.topjohnwu.core.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.DownloadModule;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.components.MarkDownWindow;
import com.topjohnwu.magisk.utils.DownloadModule;
import java.util.ArrayList;
import java.util.List;

View File

@ -8,10 +8,10 @@ import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.core.container.SuLogEntry;
import com.topjohnwu.core.database.MagiskDB;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.database.MagiskDB;
import java.util.Collections;
import java.util.HashSet;

View File

@ -1,104 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.net.Uri;
import android.view.View;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class FlashZip extends ParallelTask<Void, Void, Integer> {
private Uri mUri;
private File mCachedFile;
private List<String> console, logs;
public FlashZip(Activity context, Uri uri, List<String> console, List<String> logs) {
super(context);
mUri = uri;
this.console = console;
this.logs = logs;
mCachedFile = new File(context.getCacheDir(), "install.zip");
}
private boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
return ShellUtils.fastCmdResult("grep -q '#MAGISK' " + new File(mCachedFile.getParentFile(), "updater-script"));
}
@Override
protected Integer doInBackground(Void... voids) {
MagiskManager mm = Data.MM();
try {
console.add("- Copying zip to temp directory");
mCachedFile.delete();
try (
InputStream in = mm.getContentResolver().openInputStream(mUri);
OutputStream out = new BufferedOutputStream(new FileOutputStream(mCachedFile))
) {
if (in == null) throw new FileNotFoundException();
InputStream buf= new BufferedInputStream(in);
ShellUtils.pump(buf, out);
} catch (FileNotFoundException e) {
console.add("! Invalid Uri");
throw e;
} catch (IOException e) {
console.add("! Cannot copy to cache");
throw e;
}
if (!unzipAndCheck()) return 0;
console.add("- Installing " + Utils.getNameFromUri(mm, mUri));
if (!Shell.su("cd " + mCachedFile.getParent(),
"BOOTMODE=true sh update-binary dummy 1 " + mCachedFile)
.to(console, logs)
.exec().isSuccess())
return -1;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
console.add("- All done!");
return 1;
}
// -1 = error, manual install; 0 = invalid zip; 1 = success
@Override
protected void onPostExecute(Integer result) {
FlashActivity activity = (FlashActivity) getActivity();
Shell.su("rm -rf " + mCachedFile.getParent(), "rm -rf " + Const.TMP_FOLDER_PATH).submit();
switch (result) {
case -1:
console.add("! Installation failed");
SnackbarMaker.showUri(getActivity(), mUri);
break;
case 0:
console.add("! This zip is not a Magisk Module!");
break;
case 1:
// Reload modules
Utils.loadModules();
break;
}
activity.reboot.setVisibility(result > 0 ? View.VISIBLE : View.GONE);
activity.buttonPanel.setVisibility(View.VISIBLE);
}
}

View File

@ -1,354 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.TarEntry;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import com.topjohnwu.net.DownloadProgressListener;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.internal.NOPList;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
import com.topjohnwu.superuser.io.SuFileOutputStream;
import com.topjohnwu.utils.SignBoot;
import org.kamranzafar.jtar.TarInputStream;
import org.kamranzafar.jtar.TarOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private static final int PATCH_MODE = 0;
public static final int DIRECT_MODE = 1;
private static final int FIX_ENV_MODE = 2;
public static final int SECOND_SLOT_MODE = 3;
private Uri bootUri;
private List<String> console, logs;
private String mBoot;
private int mode;
private File installDir;
private ProgressDialog dialog;
private MagiskManager mm;
public InstallMagisk(Activity context) {
super(context);
mm = Data.MM();
mode = FIX_ENV_MODE;
}
public InstallMagisk(Activity context, List<String> console, List<String> logs, int mode) {
this(context);
this.console = console;
this.logs = logs;
this.mode = mode;
}
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri boot) {
this(context, console, logs, PATCH_MODE);
bootUri = boot;
}
@Override
protected void onPreExecute() {
if (mode == FIX_ENV_MODE) {
Activity a = getActivity();
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
console = NOPList.getInstance();
}
}
class ProgressLog implements DownloadProgressListener {
private int prev = -1;
private int location;
@Override
public void onProgress(long bytesDownloaded, long totalBytes) {
if (prev < 0) {
location = console.size();
console.add("... 0%");
}
int curr = (int) (100 * bytesDownloaded / totalBytes);
if (prev != curr) {
prev = curr;
console.set(location, "... " + prev + "%");
}
}
}
private void extractFiles(String arch) throws IOException {
File zip = new File(mm.getFilesDir(), "magisk.zip");
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
console.add("- Downloading zip");
Networking.get(Data.magiskLink)
.setDownloadProgressListener(new ProgressLog())
.execForFile(zip);
} else {
console.add("- Existing zip found");
}
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(zip), (int) zip.length());
buf.mark((int) zip.length() + 1);
console.add("- Extracting files");
try (InputStream in = buf) {
ZipUtils.unzip(in, installDir, arch + "/", true);
in.reset();
ZipUtils.unzip(in, installDir, "common/", true);
in.reset();
ZipUtils.unzip(in, installDir, "chromeos/", false);
in.reset();
ZipUtils.unzip(in, installDir, "META-INF/com/google/android/update-binary", true);
} catch (IOException e) {
console.add("! Cannot unzip zip");
throw e;
}
Shell.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
installDir, installDir, installDir)).exec();
}
private boolean dumpBoot() {
console.add("- Copying image to cache");
// Copy boot image to local
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
OutputStream out = new FileOutputStream(mBoot)
) {
if (in == null)
throw new FileNotFoundException();
InputStream src;
if (Utils.getNameFromUri(mm, bootUri).endsWith(".tar")) {
// Extract boot.img from tar
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
org.kamranzafar.jtar.TarEntry entry;
while ((entry = tar.getNextEntry()) != null) {
if (entry.getName().equals("boot.img"))
break;
}
src = tar;
} else {
// Direct copy raw image
src = new BufferedInputStream(in);
}
ShellUtils.pump(src, out);
} catch (FileNotFoundException e) {
console.add("! Invalid Uri");
return false;
} catch (IOException e) {
console.add("! Copy failed");
return false;
}
return true;
}
private File patchBoot() throws IOException {
boolean isSigned;
try (InputStream in = new SuFileInputStream(mBoot)) {
isSigned = SignBoot.verifySignature(in, null);
if (isSigned) {
console.add("- Boot image is signed with AVB 1.0");
}
} catch (IOException e) {
console.add("! Unable to check signature");
throw e;
}
// Patch boot image
if (!Shell.sh("cd " + installDir, Utils.fmt(
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep boot_patch.sh %s",
Data.keepEnc, Data.keepVerity, mBoot))
.to(console, logs).exec().isSuccess())
return null;
Shell.Job job = Shell.sh("mv bin/busybox busybox",
"rm -rf magisk.apk bin boot.img update-binary",
"cd /");
File patched = new File(installDir, "new-boot.img");
if (isSigned) {
console.add("- Signing boot image with test keys");
File signed = new File(installDir, "signed.img");
try (InputStream in = new SuFileInputStream(patched);
OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))
) {
SignBoot.doSignature("/boot", in, out, null, null);
}
job.add("mv -f " + signed + " " + patched);
}
job.exec();
return patched;
}
private boolean outputBoot(File patched) throws IOException {
switch (mode) {
case PATCH_MODE:
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + fmt);
dest.getParentFile().mkdirs();
OutputStream out;
switch (fmt) {
case ".img.tar":
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
break;
default:
case ".img":
out = new BufferedOutputStream(new FileOutputStream(dest));
break;
}
try (InputStream in = new SuFileInputStream(patched)) {
ShellUtils.pump(in, out);
out.close();
}
Shell.sh("rm -f " + patched).exec();
console.add("");
console.add("****************************");
console.add(" Patched image is placed in ");
console.add(" " + dest + " ");
console.add("****************************");
break;
case SECOND_SLOT_MODE:
case DIRECT_MODE:
if (!Shell.su(Utils.fmt("direct_install %s %s", installDir, mBoot))
.to(console, logs).exec().isSuccess())
return false;
if (!Data.keepVerity)
Shell.su("find_dtbo_image", "patch_dtbo_image").to(console, logs).exec();
break;
}
return true;
}
private void postOTA() {
SuFile bootctl = new SuFile(Const.MAGISK_PATH + "/.core/bootctl");
try (InputStream in = mm.getResources().openRawResource(R.raw.bootctl);
OutputStream out = new SuFileOutputStream(bootctl)) {
ShellUtils.pump(in, out);
Shell.su("post_ota " + bootctl.getParent()).exec();
console.add("***************************************");
console.add(" Next reboot will boot to second slot!");
console.add("***************************************");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected Boolean doInBackground(Void... voids) {
if (mode == FIX_ENV_MODE) {
installDir = new File("/data/adb/magisk");
Shell.su("rm -rf /data/adb/magisk/*").exec();
} else {
installDir = new File(
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
mm.createDeviceProtectedStorageContext() : mm)
.getFilesDir().getParent()
, "install");
Shell.sh("rm -rf " + installDir).exec();
installDir.mkdirs();
}
switch (mode) {
case PATCH_MODE:
mBoot = new File(installDir, "boot.img").getAbsolutePath();
if (!dumpBoot())
return false;
break;
case DIRECT_MODE:
console.add("- Detecting target image");
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
break;
case SECOND_SLOT_MODE:
String slot = ShellUtils.fastCmd("echo $SLOT");
String target = (TextUtils.equals(slot, "_a") ? "_b" : "_a");
console.add("- Target slot: " + target);
console.add("- Detecting target image");
mBoot = ShellUtils.fastCmd(
"SLOT=" + target,
"find_boot_image",
"SLOT=" + slot,
"echo \"$BOOTIMAGE\""
);
break;
case FIX_ENV_MODE:
mBoot = "";
break;
}
if (mBoot == null) {
console.add("! Unable to detect target image");
return false;
}
if (mode == DIRECT_MODE || mode == SECOND_SLOT_MODE)
console.add("- Target image: " + mBoot);
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
String arch = abis.contains("x86") ? "x86" : "arm";
console.add("- Device platform: " + Build.SUPPORTED_ABIS[0]);
try {
extractFiles(arch);
if (mode == FIX_ENV_MODE) {
Shell.su("fix_env").exec();
} else {
File patched = patchBoot();
if (patched == null)
return false;
if (!outputBoot(patched))
return false;
if (mode == SECOND_SLOT_MODE)
postOTA();
console.add("- All done!");
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (mode == FIX_ENV_MODE) {
dialog.dismiss();
Utils.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
} else {
// Running in FlashActivity
FlashActivity activity = (FlashActivity) getActivity();
if (!result) {
Shell.sh("rm -rf " + installDir).submit();
console.add("! Installation failed");
activity.reboot.setVisibility(View.GONE);
}
activity.buttonPanel.setVisibility(View.VISIBLE);
}
}
}

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.components;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -8,13 +9,12 @@ import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.NoUIActivity;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -26,12 +26,12 @@ import androidx.core.content.ContextCompat;
public abstract class BaseActivity extends AppCompatActivity implements Topic.AutoSubscriber {
public static final String INTENT_PERM = "perm_dialog";
private static Runnable grantCallback;
protected static Runnable permissionGrantCallback;
static int[] EMPTY_INT_ARRAY = new int[0];
private ActivityResultListener activityResultListener;
public MagiskManager mm;
public App app;
@Override
public int[] getSubscribedTopics() {
@ -49,7 +49,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
Configuration config = base.getResources().getConfiguration();
config.setLocale(LocaleManager.locale);
applyOverrideConfiguration(config);
mm = Data.MM();
app = App.self;
}
@Override
@ -84,6 +84,14 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
}
}
public void runWithExternalRW(Runnable callback) {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, callback);
}
public void runWithPermission(String[] permissions, Runnable callback) {
runWithPermission(this, permissions, callback);
}
public static void runWithPermission(Context context, String[] permissions, Runnable callback) {
boolean granted = true;
for (String perm : permissions) {
@ -95,23 +103,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
callback.run();
} else {
// Passed in context should be an activity if not granted, need to show dialog!
permissionGrantCallback = callback;
if (!(context instanceof BaseActivity)) {
// Start NoUIActivity to show dialog
Intent intent = new Intent(context, Data.classMap.get(NoUIActivity.class));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(INTENT_PERM, permissions);
context.startActivity(intent);
} else {
if (context instanceof BaseActivity) {
grantCallback = callback;
ActivityCompat.requestPermissions((BaseActivity) context, permissions, 0);
}
}
}
public void runWithPermission(String[] permissions, Runnable callback) {
runWithPermission(this, permissions, callback);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (activityResultListener != null)
@ -132,13 +130,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
grant = false;
}
if (grant) {
if (permissionGrantCallback != null) {
permissionGrantCallback.run();
if (grantCallback != null) {
grantCallback.run();
}
} else {
Toast.makeText(this, R.string.no_rw_storage, Toast.LENGTH_LONG).show();
}
permissionGrantCallback = null;
grantCallback = null;
}
public interface ActivityResultListener {

View File

@ -2,20 +2,19 @@ package com.topjohnwu.magisk.components;
import android.content.Intent;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.core.App;
import com.topjohnwu.core.utils.Topic;
import androidx.fragment.app.Fragment;
import butterknife.Unbinder;
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
public MagiskManager mm;
public App app;
protected Unbinder unbinder = null;
public BaseFragment() {
mm = Data.MM();
app = App.self;
}
@Override

View File

@ -1,9 +1,14 @@
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.app.ProgressDialog;
import android.widget.Toast;
import com.topjohnwu.core.tasks.MagiskInstaller;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import androidx.annotation.NonNull;
@ -14,7 +19,25 @@ public class EnvFixDialog extends CustomAlertDialog {
setTitle(R.string.env_fix_title);
setMessage(R.string.env_fix_msg);
setCancelable(true);
setPositiveButton(R.string.yes, (d, i) -> new InstallMagisk(activity).exec());
setPositiveButton(R.string.yes, (d, i) -> {
ProgressDialog pd = ProgressDialog.show(activity,
activity.getString(R.string.setup_title),
activity.getString(R.string.setup_msg));
new MagiskInstaller() {
@Override
protected boolean operations() {
installDir = new SuFile("/data/adb/magisk");
Shell.su("rm -rf /data/adb/magisk/*").exec();
return extractZip() && Shell.su("fix_env").exec().isSuccess();
}
@Override
protected void onResult(boolean success) {
pd.dismiss();
Utils.toast(success ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
}
}.exec();
});
setNegativeButton(R.string.no_thanks, null);
}
}

View File

@ -1,16 +1,16 @@
package com.topjohnwu.magisk.components;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.widget.Toast;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import java.io.File;
@ -33,7 +33,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
downloadOnly(activity);
break;
case 2:
intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
intent = new Intent(activity, ClassMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
activity.startActivity(intent);
break;
@ -48,13 +48,13 @@ class InstallMethodDialog extends AlertDialog.Builder {
private void patchBoot(BaseActivity a) {
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
a.runWithPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, () ->
a.runWithExternalRW(() ->
a.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == Activity.RESULT_OK && data != null) {
Intent i = new Intent(a, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
Intent i = new Intent(a, ClassMap.get(FlashActivity.class))
.setData(data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
a.startActivity(i);
}
@ -63,7 +63,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
}
private void downloadOnly(BaseActivity a) {
a.runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
a.runWithExternalRW(() -> {
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
File zip = new File(Const.EXTERNAL_PATH, filename);
@ -86,7 +86,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
.setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> {
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
Intent intent = new Intent(activity, ClassMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(intent);
})

View File

@ -3,11 +3,10 @@ package com.topjohnwu.magisk.components;
import android.net.Uri;
import android.text.TextUtils;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.AppUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@ -15,35 +14,34 @@ import java.util.ArrayList;
import java.util.List;
public class MagiskInstallDialog extends CustomAlertDialog {
public MagiskInstallDialog(BaseActivity activity) {
super(activity);
MagiskManager mm = Data.MM();
public MagiskInstallDialog(BaseActivity a) {
super(a);
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)));
setMessage(mm.getString(R.string.repo_install_msg, filename));
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk)));
setMessage(a.getString(R.string.repo_install_msg, filename));
setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> {
List<String> options = new ArrayList<>();
options.add(mm.getString(R.string.download_zip_only));
options.add(mm.getString(R.string.patch_boot_file));
options.add(a.getString(R.string.download_zip_only));
options.add(a.getString(R.string.patch_boot_file));
if (Shell.rootAccess()) {
options.add(mm.getString(R.string.direct_install));
options.add(a.getString(R.string.direct_install));
String s = ShellUtils.fastCmd("grep_prop ro.build.ab_update");
if (!s.isEmpty() && Boolean.parseBoolean(s)) {
options.add(mm.getString(R.string.install_inactive_slot));
options.add(a.getString(R.string.install_inactive_slot));
}
}
new InstallMethodDialog(activity, options).show();
new InstallMethodDialog(a, options).show();
});
setNegativeButton(R.string.no_thanks, null);
if (!TextUtils.isEmpty(Data.magiskNoteLink)) {
setNeutralButton(R.string.release_notes, (d, i) -> {
if (Data.magiskNoteLink.contains("forum.xda-developers")) {
// Open forum links in browser
Utils.openLink(activity, Uri.parse(Data.magiskNoteLink));
AppUtils.openLink(a, Uri.parse(Data.magiskNoteLink));
} else {
new MarkDownWindow(activity, null, Data.magiskNoteLink).exec();
new MarkDownWindow(a, null, Data.magiskNoteLink).exec();
}
});
}

View File

@ -2,30 +2,27 @@ package com.topjohnwu.magisk.components;
import android.text.TextUtils;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.DownloadApp;
import androidx.annotation.NonNull;
public class ManagerInstallDialog extends CustomAlertDialog {
public ManagerInstallDialog(@NonNull BaseActivity activity) {
super(activity);
MagiskManager mm = Data.MM();
public ManagerInstallDialog(@NonNull BaseActivity a) {
super(a);
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)));
setMessage(mm.getString(R.string.repo_install_msg, name));
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name)));
setMessage(a.getString(R.string.repo_install_msg, name));
setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> DlInstallManager.upgrade(name));
setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name));
setNegativeButton(R.string.no_thanks, null);
if (!TextUtils.isEmpty(Data.managerNoteLink)) {
setNeutralButton(R.string.app_changelog, (d, i) ->
new MarkDownWindow(activity, null, Data.managerNoteLink).exec());
new MarkDownWindow(a, null, Data.managerNoteLink).exec());
}
}
}

View File

@ -1,12 +1,13 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.webkit.WebView;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.ParallelTask;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.ShellUtils;
import org.commonmark.node.Node;
@ -40,7 +41,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
MagiskManager mm = Data.MM();
App app = App.self;
String md;
if (mUrl != null) {
md = Utils.dlString(mUrl);
@ -55,14 +56,11 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
}
}
String css;
try (
InputStream in = mm.getResources().openRawResource(
Data.isDarkTheme ? R.raw.dark : R.raw.light);
ByteArrayOutputStream out = new ByteArrayOutputStream()
) {
try (InputStream in = app.getResources()
.openRawResource(Data.isDarkTheme ? R.raw.dark : R.raw.light);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
ShellUtils.pump(in, out);
css = out.toString();
in.close();
} catch (IOException e) {
e.printStackTrace();
return "";

View File

@ -7,13 +7,14 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.utils.Utils;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@ -36,74 +37,74 @@ public class Notifications {
}
public static void magiskUpdate() {
MagiskManager mm = Data.MM();
App app = App.self;
Intent intent = new Intent(mm, Data.classMap.get(SplashActivity.class));
Intent intent = new Intent(app, ClassMap.get(SplashActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
stackBuilder.addParentStack(Data.classMap.get(SplashActivity.class));
TaskStackBuilder stackBuilder = TaskStackBuilder.create(app);
stackBuilder.addParentStack(ClassMap.get(SplashActivity.class));
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.magisk_update_title))
.setContentText(mm.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
.setContentTitle(app.getString(R.string.magisk_update_title))
.setContentText(app.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void managerUpdate() {
MagiskManager mm = Data.MM();
App app = App.self;
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class));
Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class));
intent.setAction(Const.Key.BROADCAST_MANAGER_UPDATE);
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
intent.putExtra(Const.Key.INTENT_SET_NAME, name);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
PendingIntent pendingIntent = PendingIntent.getBroadcast(app,
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.manager_update_title))
.setContentText(mm.getString(R.string.manager_download_install))
.setContentTitle(app.getString(R.string.manager_update_title))
.setContentText(app.getString(R.string.manager_download_install))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void dtboPatched() {
MagiskManager mm = Data.MM();
App app = App.self;
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class))
Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class))
.setAction(Const.Key.BROADCAST_REBOOT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
PendingIntent pendingIntent = PendingIntent.getBroadcast(app,
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
.setContentTitle(app.getString(R.string.dtbo_patched_title))
.setContentText(app.getString(R.string.dtbo_patched_reboot))
.setVibrate(new long[]{0, 100, 100, 100})
.addAction(R.drawable.ic_refresh, mm.getString(R.string.reboot), pendingIntent);
.addAction(R.drawable.ic_refresh, app.getString(R.string.reboot), pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
}
public static NotificationCompat.Builder progress(String title) {
MagiskManager mm = Data.MM();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.PROGRESS_NOTIFICATION_CHANNEL);
App app = App.self;
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.PROGRESS_NOTIFICATION_CHANNEL);
builder.setPriority(NotificationCompat.PRIORITY_LOW)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(title)

View File

@ -2,10 +2,9 @@ package com.topjohnwu.magisk.components;
import android.widget.Toast;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.DownloadProgressListener;
import androidx.core.app.NotificationCompat;
@ -17,12 +16,11 @@ public class ProgressNotification implements DownloadProgressListener {
private long prevTime;
public ProgressNotification(String title) {
MagiskManager mm = Data.MM();
mgr = NotificationManagerCompat.from(mm);
mgr = NotificationManagerCompat.from(App.self);
builder = Notifications.progress(title);
prevTime = System.currentTimeMillis();
update();
Utils.toast(mm.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
Utils.toast(App.self.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
}
@Override
@ -47,7 +45,7 @@ public class ProgressNotification implements DownloadProgressListener {
public void dlDone() {
builder.setProgress(0, 0, false)
.setContentText(Data.MM().getString(R.string.download_complete))
.setContentText(App.self.getString(R.string.download_complete))
.setSmallIcon(R.drawable.ic_check_circle)
.setOngoing(false);
update();
@ -55,7 +53,7 @@ public class ProgressNotification implements DownloadProgressListener {
public void dlFail() {
builder.setProgress(0, 0, false)
.setContentText(Data.MM().getString(R.string.download_file_error))
.setContentText(App.self.getString(R.string.download_file_error))
.setSmallIcon(R.drawable.ic_cancel)
.setOngoing(false);
update();

View File

@ -6,8 +6,8 @@ import android.view.View;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.StringRes;

View File

@ -7,11 +7,12 @@ import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
@ -47,7 +48,7 @@ public class UninstallDialog extends CustomAlertDialog {
.setErrorHandler(((conn, e) -> progress.dlFail()))
.getAsFile(f -> {
progress.dismiss();
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
Intent intent = new Intent(activity, ClassMap.get(FlashActivity.class))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.fromFile(f))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);

View File

@ -15,13 +15,15 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.core.tasks.SafetyNet;
import com.topjohnwu.core.utils.ISafetyNetHelper;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.SafetyNet;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.CustomAlertDialog;
@ -31,8 +33,6 @@ import com.topjohnwu.magisk.components.MagiskInstallDialog;
import com.topjohnwu.magisk.components.ManagerInstallDialog;
import com.topjohnwu.magisk.components.UninstallDialog;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@ -119,7 +119,7 @@ public class MagiskFragment extends BaseFragment
return;
}
((NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
((NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
new MagiskInstallDialog((BaseActivity) getActivity()).show();
}
@ -169,7 +169,7 @@ public class MagiskFragment extends BaseFragment
shownDialog = false;
// Trigger state check
if (Download.checkNetworkStatus(mm)) {
if (Download.checkNetworkStatus(app)) {
CheckUpdates.check();
} else {
mSwipeRefreshLayout.setRefreshing(false);
@ -199,7 +199,7 @@ public class MagiskFragment extends BaseFragment
}
private boolean hasGms() {
PackageManager pm = mm.getPackageManager();
PackageManager pm = app.getPackageManager();
PackageInfo info;
try {
info = pm.getPackageInfo("com.google.android.gms", 0);
@ -212,13 +212,13 @@ public class MagiskFragment extends BaseFragment
private void updateUI() {
((MainActivity) requireActivity()).checkHideSection();
boolean hasNetwork = Download.checkNetworkStatus(mm);
boolean hasNetwork = Download.checkNetworkStatus(app);
boolean hasRoot = Shell.rootAccess();
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(hasRoot ? View.VISIBLE : View.GONE);
coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
coreOnlyNotice.setVisibility(app.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
int image, color;

View File

@ -8,10 +8,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.utils.Topic;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View File

@ -15,11 +15,11 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.io.File;

View File

@ -12,15 +12,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@ -94,7 +94,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
Intent intent = new Intent(getActivity(), Data.classMap.get(FlashActivity.class));
Intent intent = new Intent(getActivity(), ClassMap.get(FlashActivity.class));
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
startActivity(intent);
}

View File

@ -11,14 +11,14 @@ import android.view.ViewGroup;
import android.widget.SearchView;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.tasks.UpdateRepos;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Topic;
import java.util.Map;
@ -70,8 +70,8 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public void onPublish(int topic, Object[] result) {
if (topic == Topic.MODULE_LOAD_DONE) {
adapter = new ReposAdapter(mm.repoDB, (Map<String, Module>) result[0]);
mm.repoDB.registerAdapter(adapter);
adapter = new ReposAdapter(app.repoDB, (Map<String, Module>) result[0]);
app.repoDB.registerAdapterCallback(adapter::notifyDBChanged);
recyclerView.setAdapter(adapter);
recyclerView.setVisibility(View.VISIBLE);
emptyRv.setVisibility(View.GONE);
@ -108,7 +108,7 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
.setTitle(R.string.sorting_order)
.setSingleChoiceItems(R.array.sorting_orders, Data.repoOrder, (d, which) -> {
Data.repoOrder = which;
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, Data.repoOrder).apply();
app.prefs.edit().putInt(Const.Key.REPO_ORDER, Data.repoOrder).apply();
adapter.notifyDBChanged();
d.dismiss();
}).show();
@ -119,6 +119,6 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public void onDestroyView() {
super.onDestroyView();
mm.repoDB.unregisterAdapter();
app.repoDB.unregisterAdapterCallback();
}
}

View File

@ -10,19 +10,20 @@ import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.AppUtils;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.DownloadApp;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.PatchAPK;
import com.topjohnwu.superuser.Shell;
import java.io.IOException;
@ -43,7 +44,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener,
Topic.Subscriber, Topic.AutoSubscriber {
private MagiskManager mm;
private App app = App.self;
private ListPreference updateChannel, autoRes, suNotification,
requestTimeout, rootConfig, multiuserConfig, nsConfig;
@ -52,10 +53,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
private boolean showSuperuser;
private void prefsSync() {
rootState = mm.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
namespaceState = mm.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
rootState = app.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
namespaceState = app.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
showSuperuser = Utils.showSuperUser();
mm.prefs.edit()
app.prefs.edit()
.putString(Const.Key.ROOT_ACCESS, String.valueOf(rootState))
.putString(Const.Key.SU_MNT_NS, String.valueOf(namespaceState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(Data.multiuserState))
@ -65,7 +66,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
mm = Data.MM();
prefsSync();
setPreferencesFromResource(R.xml.app_settings, rootKey);
@ -82,12 +82,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
});
Preference restoreManager = findPreference("restore");
restoreManager.setOnPreferenceClickListener(pref -> {
DlInstallManager.restore();
DownloadApp.restore();
return true;
});
findPreference("clear").setOnPreferenceClickListener(pref -> {
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
mm.repoDB.clearRepo();
app.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
app.repoDB.clearRepo();
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
return true;
});
@ -114,17 +114,17 @@ public class SettingsFragment extends PreferenceFragmentCompat
if (channel == Const.Value.CUSTOM_CHANNEL) {
View v = LayoutInflater.from(requireActivity()).inflate(R.layout.custom_channel_dialog, null);
EditText url = v.findViewById(R.id.custom_url);
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
url.setText(app.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
new AlertDialog.Builder(requireActivity())
.setTitle(R.string.settings_update_custom)
.setView(v)
.setPositiveButton(R.string.ok, (d, i) ->
mm.prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
app.prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
url.getText().toString()).apply())
.setNegativeButton(R.string.close, (d, i) ->
mm.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
app.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
.setOnCancelListener(d ->
mm.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
app.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
.show();
}
return true;
@ -152,10 +152,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
if (Shell.rootAccess() && Const.USER_ID == 0) {
if (mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
if (app.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
generalCatagory.removePreference(restoreManager);
} else {
if (!Download.checkNetworkStatus(mm))
if (!Download.checkNetworkStatus(app))
generalCatagory.removePreference(restoreManager);
generalCatagory.removePreference(hideManager);
}
@ -191,7 +191,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mm.prefs.registerOnSharedPreferenceChangeListener(this);
app.prefs.registerOnSharedPreferenceChangeListener(this);
Topic.subscribe(this);
requireActivity().setTitle(R.string.settings);
return super.onCreateView(inflater, container, savedInstanceState);
@ -199,7 +199,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public void onDestroyView() {
mm.prefs.unregisterOnSharedPreferenceChangeListener(this);
app.prefs.unregisterOnSharedPreferenceChangeListener(this);
Topic.unsubscribe(this);
super.onDestroyView();
}
@ -210,7 +210,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
case Const.Key.ROOT_ACCESS:
case Const.Key.SU_MULTIUSER_MODE:
case Const.Key.SU_MNT_NS:
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
app.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
break;
}
switch (key) {
@ -244,7 +244,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
break;
case Const.Key.LOCALE:
LocaleManager.setLocale(mm);
LocaleManager.setLocale(app);
requireActivity().recreate();
break;
case Const.Key.UPDATE_CHANNEL:
@ -252,7 +252,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
CheckUpdates.check();
break;
case Const.Key.CHECK_UPDATES:
Utils.setupUpdateCheck();
AppUtils.setupUpdateCheck();
break;
}
Data.loadConfig();
@ -268,7 +268,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
((SwitchPreference) preference).setChecked(!checked);
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
((SwitchPreference) preference).setChecked(checked);
mm.mDB.setSettings(key, checked ? 1 : 0);
app.mDB.setSettings(key, checked ? 1 : 0);
});
break;
}
@ -286,7 +286,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
.getStringArray(R.array.su_notification)[Data.suNotificationType]);
requestTimeout.setSummary(
getString(R.string.request_timeout_summary,
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
app.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
multiuserConfig.setSummary(getResources()
.getStringArray(R.array.multiuser_summary)[Data.multiuserState]);
nsConfig.setSummary(getResources()

View File

@ -42,7 +42,7 @@ public class SuLogFragment extends BaseFragment {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = new SuLogFragment_ViewBinding(this, v);
adapter = new SuLogAdapter(mm.mDB);
adapter = new SuLogAdapter(app.mDB);
recyclerView.setAdapter(adapter);
updateList();
@ -69,7 +69,7 @@ public class SuLogFragment extends BaseFragment {
updateList();
return true;
case R.id.menu_clear:
mm.mDB.clearLogs();
app.mDB.clearLogs();
updateList();
return true;
default:

View File

@ -7,10 +7,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Policy;
import java.util.List;
@ -48,13 +48,13 @@ public class SuperuserFragment extends BaseFragment {
}
private void displayPolicyList() {
List<Policy> policyList = mm.mDB.getPolicyList();
List<Policy> policyList = app.mDB.getPolicyList();
if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(new PolicyAdapter(policyList, mm.mDB, pm));
recyclerView.setAdapter(new PolicyAdapter(policyList, app.mDB, pm));
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}

View File

@ -4,12 +4,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.DownloadApp;
import com.topjohnwu.magisk.utils.SuConnector;
import com.topjohnwu.superuser.Shell;
@ -21,7 +22,7 @@ public class GeneralReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
MagiskManager mm = Data.MM();
App app = App.self;
if (intent == null)
return;
String action = intent.getAction();
@ -34,10 +35,10 @@ public class GeneralReceiver extends BroadcastReceiver {
bootAction = "boot";
switch (bootAction) {
case "request":
Intent i = new Intent(mm, Data.classMap.get(SuRequestActivity.class))
Intent i = new Intent(app, ClassMap.get(SuRequestActivity.class))
.putExtra("socket", intent.getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mm.startActivity(i);
app.startActivity(i);
break;
case "log":
SuConnector.handleLogs(intent);
@ -48,24 +49,24 @@ public class GeneralReceiver extends BroadcastReceiver {
case "boot":
default:
/* The actual on-boot trigger */
OnBootService.enqueueWork(mm);
OnBootService.enqueueWork(app);
break;
}
break;
case Intent.ACTION_PACKAGE_REPLACED:
// This will only work pre-O
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
mm.mDB.deletePolicy(getPkg(intent));
if (app.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
app.mDB.deletePolicy(getPkg(intent));
}
break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
String pkg = getPkg(intent);
mm.mDB.deletePolicy(pkg);
app.mDB.deletePolicy(pkg);
Shell.su("magiskhide --rm " + pkg).submit();
break;
case Const.Key.BROADCAST_MANAGER_UPDATE:
Data.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK);
DlInstallManager.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME));
DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME));
break;
case Const.Key.BROADCAST_REBOOT:
Shell.su("/system/bin/reboot").submit();

View File

@ -8,12 +8,13 @@ import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Build;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@ -24,55 +25,54 @@ public class ShortcutReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
MagiskManager mm = Data.MM();
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
manager.setDynamicShortcuts(getShortCuts(mm));
manager.setDynamicShortcuts(getShortCuts(App.self));
}
}
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
private ArrayList<ShortcutInfo> getShortCuts(App app) {
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
boolean root = Shell.rootAccess();
if (Utils.showSuperUser()) {
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
.setShortLabel(mm.getString(R.string.superuser))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
shortCuts.add(new ShortcutInfo.Builder(app, "superuser")
.setShortLabel(app.getString(R.string.superuser))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "superuser")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_superuser))
.setIcon(Icon.createWithResource(app, R.drawable.sc_superuser))
.setRank(0)
.build());
}
if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
.setShortLabel(mm.getString(R.string.magiskhide))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
if (root && app.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(app, "magiskhide")
.setShortLabel(app.getString(R.string.magiskhide))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_magiskhide))
.setIcon(Icon.createWithResource(app, R.drawable.sc_magiskhide))
.setRank(1)
.build());
}
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
.setShortLabel(mm.getString(R.string.modules))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
if (!app.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(app, "modules")
.setShortLabel(app.getString(R.string.modules))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "modules")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_extension))
.setIcon(Icon.createWithResource(app, R.drawable.sc_extension))
.setRank(3)
.build());
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
.setShortLabel(mm.getString(R.string.downloads))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
shortCuts.add(new ShortcutInfo.Builder(app, "downloads")
.setShortLabel(app.getString(R.string.downloads))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "downloads")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_cloud_download))
.setIcon(Icon.createWithResource(app, R.drawable.sc_cloud_download))
.setRank(2)
.build());
}

View File

@ -3,8 +3,8 @@ package com.topjohnwu.magisk.services;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@ -15,7 +15,7 @@ import androidx.core.app.JobIntentService;
public class OnBootService extends JobIntentService {
public static void enqueueWork(Context context) {
enqueueWork(context, Data.classMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
enqueueWork(context, ClassMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
}
@Override

View File

@ -3,7 +3,10 @@ package com.topjohnwu.magisk.services;
import android.app.job.JobParameters;
import android.app.job.JobService;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.superuser.Shell;
public class UpdateCheckService extends JobService {
@ -11,7 +14,14 @@ public class UpdateCheckService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Shell.getShell();
CheckUpdates.check(() -> jobFinished(params, false));
CheckUpdates.check(() -> {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
Notifications.managerUpdate();
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
Notifications.magiskUpdate();
}
jobFinished(params, false);
});
return true;
}

View File

@ -0,0 +1,50 @@
package com.topjohnwu.magisk.utils;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.services.UpdateCheckService;
public class AppUtils {
public static void setupUpdateCheck() {
App app = App.self;
JobScheduler scheduler = (JobScheduler) app.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (app.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > app.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(app, ClassMap.get(UpdateCheckService.class));
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(8 * 60 * 60 * 1000)
.build();
scheduler.schedule(info);
}
} else {
scheduler.cancel(Const.UPDATE_SERVICE_VER);
}
}
public static void openLink(Context context, Uri link) {
Intent intent = new Intent(Intent.ACTION_VIEW, link);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
} else {
Utils.toast(R.string.open_link_failed_toast, Toast.LENGTH_SHORT);
}
}
}

View File

@ -2,11 +2,12 @@ package com.topjohnwu.magisk.utils;
import android.os.AsyncTask;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.RootUtils;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
@ -18,7 +19,7 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class DlInstallManager {
public class DownloadApp {
public static void upgrade(String name) {
dlInstall(name, new PatchPackageName());
@ -31,8 +32,7 @@ public class DlInstallManager {
}
private static void dlInstall(String name, ManagerDownloadListener listener) {
MagiskManager mm = Data.MM();
File apk = new File(mm.getFilesDir(), "manager.apk");
File apk = new File(App.self.getFilesDir(), "manager.apk");
ProgressNotification progress = new ProgressNotification(name);
listener.setProgressNotification(progress);
Networking.get(Data.managerLink)
@ -63,23 +63,23 @@ public class DlInstallManager {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
File patched = apk;
MagiskManager mm = Data.MM();
if (!mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
App app = App.self;
if (!App.self.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
progress.getNotification()
.setProgress(0, 0, true)
.setContentTitle(mm.getString(R.string.hide_manager_title))
.setContentTitle(app.getString(R.string.hide_manager_title))
.setContentText("");
progress.update();
patched = new File(apk.getParent(), "patched.apk");
try {
JarMap jarMap = new JarMap(apk);
PatchAPK.patch(jarMap, mm.getPackageName());
PatchAPK.patch(jarMap, app.getPackageName());
SignAPK.sign(jarMap, new BufferedOutputStream(new FileOutputStream(patched)));
} catch (Exception e) {
return;
}
}
APKInstall.install(mm, patched);
APKInstall.install(app, patched);
progress.dismiss();
}
}
@ -88,17 +88,17 @@ public class DlInstallManager {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
MagiskManager mm = Data.MM();
App app = App.self;
progress.getNotification()
.setProgress(0, 0, true)
.setContentTitle(mm.getString(R.string.restore_img_msg))
.setContentTitle(app.getString(R.string.restore_img_msg))
.setContentText("");
progress.update();
Data.exportPrefs();
// Make it world readable
apk.setReadable(true, false);
if (ShellUtils.fastCmdResult("pm install " + apk))
RootUtils.rmAndLaunch(mm.getPackageName(), BuildConfig.APPLICATION_ID);
RootUtils.rmAndLaunch(app.getPackageName(), BuildConfig.APPLICATION_ID);
progress.dismiss();
}
}

View File

@ -1,17 +1,16 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.magisk.utils;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.container.Repo;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.ShellUtils;
@ -28,26 +27,25 @@ import java.util.zip.ZipOutputStream;
public class DownloadModule {
public static void exec(BaseActivity activity, Repo repo, boolean install) {
activity.runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> dlProcessInstall(repo, install)));
activity.runWithExternalRW(() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(
() -> dlProcessInstall(repo, install)));
}
private static void dlProcessInstall(Repo repo, boolean install) {
File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
ProgressNotification progress = new ProgressNotification(output.getName());
try {
MagiskManager mm = Data.MM();
InputStream in = Networking.get(repo.getZipUrl())
.setDownloadProgressListener(progress)
.execForInputStream().getResult();
removeTopFolder(in, new BufferedOutputStream(new FileOutputStream(output)));
if (install) {
progress.dismiss();
Intent intent = new Intent(mm, Data.classMap.get(FlashActivity.class));
Intent intent = new Intent(App.self, ClassMap.get(FlashActivity.class));
intent.setData(Uri.fromFile(output))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
mm.startActivity(intent);
App.self.startActivity(intent);
} else {
progress.getNotification().setContentTitle(output.getName());
progress.dlDone();

View File

@ -16,9 +16,9 @@ import android.security.keystore.KeyProperties;
import android.view.Gravity;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
@ -36,10 +36,9 @@ public abstract class FingerprintHelper {
private CancellationSignal cancel;
public static boolean useFingerPrint() {
MagiskManager mm = Data.MM();
boolean fp = mm.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
boolean fp = App.self.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
if (fp && !canUseFingerprint()) {
mm.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
App.self.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
fp = false;
}
return fp;
@ -48,9 +47,8 @@ public abstract class FingerprintHelper {
public static boolean canUseFingerprint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return false;
MagiskManager mm = Data.MM();
KeyguardManager km = mm.getSystemService(KeyguardManager.class);
FingerprintManager fm = mm.getSystemService(FingerprintManager.class);
KeyguardManager km = App.self.getSystemService(KeyguardManager.class);
FingerprintManager fm = App.self.getSystemService(FingerprintManager.class);
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
}
@ -106,7 +104,7 @@ public abstract class FingerprintHelper {
protected FingerprintHelper() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = Data.MM().getSystemService(FingerprintManager.class);
manager = App.self.getSystemService(FingerprintManager.class);
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);

View File

@ -1,16 +1,16 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.magisk.utils;
import android.os.AsyncTask;
import android.widget.Toast;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.RootUtils;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK;
@ -95,14 +95,14 @@ public class PatchAPK {
}
private static boolean patchAndHide() {
MagiskManager mm = Data.MM();
App app = App.self;
// Generate a new app with random package name
File repack = new File(mm.getFilesDir(), "patched.apk");
File repack = new File(app.getFilesDir(), "patched.apk");
String pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length());
try {
JarMap apk = new JarMap(mm.getPackageCodePath());
JarMap apk = new JarMap(app.getPackageCodePath());
if (!patch(apk, pkg))
return false;
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(repack)));
@ -115,7 +115,7 @@ public class PatchAPK {
if (!ShellUtils.fastCmdResult("pm install " + repack))
return false;;
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
app.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
Data.exportPrefs();
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, pkg);
@ -143,10 +143,10 @@ public class PatchAPK {
public static void hideManager() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
MagiskManager mm = Data.MM();
App app = App.self;
NotificationCompat.Builder progress =
Notifications.progress(mm.getString(R.string.hide_manager_title));
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
Notifications.progress(app.getString(R.string.hide_manager_title));
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build());
boolean b = patchAndHide();
mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);

View File

@ -9,12 +9,13 @@ import android.os.Process;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.core.container.SuLogEntry;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@ -76,8 +77,8 @@ public abstract class SuConnector {
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
MagiskManager mm = Data.MM();
PackageManager pm = mm.getPackageManager();
App app = App.self;
PackageManager pm = app.getPackageManager();
Policy policy;
boolean notify;
@ -91,7 +92,7 @@ public abstract class SuConnector {
}
} else {
// Doesn't report whether notify or not, check database ourselves
policy = mm.mDB.getPolicy(fromUid);
policy = app.mDB.getPolicy(fromUid);
if (policy == null)
return;
notify = policy.notification;
@ -116,24 +117,22 @@ public abstract class SuConnector {
log.fromPid = pid;
log.command = command;
log.date = new Date();
mm.mDB.addLog(log);
app.mDB.addLog(log);
}
private static void handleNotify(Policy policy) {
MagiskManager mm = Data.MM();
String message = mm.getString(policy.policy == Policy.ALLOW ?
String message = App.self.getString(policy.policy == Policy.ALLOW ?
R.string.su_allow_toast : R.string.su_deny_toast, policy.appName);
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
Utils.toast(message, Toast.LENGTH_SHORT);
}
public static void handleNotify(Intent intent) {
MagiskManager mm = Data.MM();
int fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
try {
Policy policy = new Policy(fromUid, mm.getPackageManager());
Policy policy = new Policy(fromUid, App.self.getPackageManager());
policy.policy = intent.getIntExtra("policy", -1);
if (policy.policy >= 0)
handleNotify(policy);

View File

@ -6,12 +6,6 @@ import android.net.NetworkInfo;
public class Download {
public static String getLegalFilename(CharSequence filename) {
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
.replace("$", "").replace("`", "").replace("*", "").replace("/", "_")
.replace("#", "").replace("@", "").replace("\\", "_");
}
public static boolean checkNetworkStatus(Context context) {
ConnectivityManager manager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);

View File

@ -242,7 +242,7 @@ def build_apk(args, flavor):
def build_app(args):
source = os.path.join('scripts', 'util_functions.sh')
target = os.path.join('app', 'src', 'full', 'res', 'raw', 'util_functions.sh')
target = os.path.join('core', 'src', 'main', 'res', 'raw', 'util_functions.sh')
cp(source, target)
build_apk(args, 'Full')

2
core/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build
src/main/res/raw/util_functions.sh

33
core/build.gradle Normal file
View File

@ -0,0 +1,33 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion 16
targetSdkVersion rootProject.ext.compileSdkVersion
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':net')
implementation project(':utils')
implementation 'com.github.topjohnwu:libsu:2.1.2'
implementation 'org.kamranzafar:jtar:2.3'
}

21
core/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.topjohnwu.core" />

View File

@ -1,30 +1,31 @@
package com.topjohnwu.magisk;
package com.topjohnwu.core;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import com.topjohnwu.magisk.database.MagiskDB;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.core.database.MagiskDB;
import com.topjohnwu.core.database.RepoDatabaseHelper;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.RootUtils;
import com.topjohnwu.superuser.ContainerApp;
import com.topjohnwu.superuser.Shell;
import java.lang.ref.WeakReference;
public class App extends ContainerApp {
public class MagiskManager extends ContainerApp {
// Info
public boolean hasInit = false;
public static App self;
public static Handler mainHandler = new Handler(Looper.getMainLooper());
public boolean init = false;
// Global resources
public SharedPreferences prefs;
public RepoDatabaseHelper repoDB;
public MagiskDB mDB;
public RepoDatabaseHelper repoDB;
public MagiskManager() {
Data.weakApp = new WeakReference<>(this);
public App() {
self = this;
}
@Override
@ -37,8 +38,8 @@ public class MagiskManager extends ContainerApp {
Shell.Config.setTimeout(2);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
repoDB = new RepoDatabaseHelper(this);
mDB = new MagiskDB(this);
repoDB = new RepoDatabaseHelper(this);
LocaleManager.setLocale(this);
Data.loadConfig();

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk;
package com.topjohnwu.core;
import android.os.Environment;
import android.os.Process;
@ -39,7 +39,7 @@ public class Const {
/* A list of apps that should not be shown as hide-able */
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
Data.MM().getPackageName(),
App.self.getPackageName(),
"com.google.android.gms"
);

View File

@ -1,16 +1,9 @@
package com.topjohnwu.magisk;
package com.topjohnwu.core;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.util.Xml;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;
@ -21,15 +14,8 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class Data {
// Global app instance
public static WeakReference<MagiskManager> weakApp;
public static Handler mainHandler = new Handler(Looper.getMainLooper());
public static Map<Class, Class> classMap = new HashMap<>();
// Current status
public static String magiskVersionString;
@ -55,29 +41,12 @@ public class Data {
// Configs
public static boolean isDarkTheme;
public static int suRequestTimeout;
public static int suLogTimeout = 14;
public static int multiuserState = -1;
public static int suResponseType;
public static int suNotificationType;
public static int updateChannel;
public static int repoOrder;
static {
classMap.put(MagiskManager.class, a.q.class);
classMap.put(MainActivity.class, a.b.class);
classMap.put(SplashActivity.class, a.c.class);
classMap.put(AboutActivity.class, a.d.class);
classMap.put(DonationActivity.class, a.e.class);
classMap.put(FlashActivity.class, a.f.class);
classMap.put(NoUIActivity.class, a.g.class);
classMap.put(GeneralReceiver.class, a.h.class);
classMap.put(ShortcutReceiver.class, a.i.class);
classMap.put(OnBootService.class, a.j.class);
classMap.put(UpdateCheckService.class, a.k.class);
classMap.put(AboutCardRow.class, a.l.class);
classMap.put(SuRequestActivity.class, a.m.class);
}
public static int suLogTimeout = 14;
public static void loadMagiskInfo() {
try {
@ -87,24 +56,19 @@ public class Data {
} catch (NumberFormatException ignored) {}
}
public static MagiskManager MM() {
return weakApp.get();
}
public static void exportPrefs() {
// Flush prefs to disk
MagiskManager mm = MM();
mm.prefs.edit().commit();
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
mm.getPackageName() + "_preferences.xml");
App app = App.self;
app.prefs.edit().commit();
File xml = new File(app.getFilesDir().getParent() + "/shared_prefs",
app.getPackageName() + "_preferences.xml");
Shell.su(Utils.fmt("cat %s > /data/user/0/%s", xml, Const.MANAGER_CONFIGS)).exec();
}
public static void importPrefs() {
MagiskManager mm = MM();
SuFile config = new SuFile("/data/user/0/" + Const.MANAGER_CONFIGS);
if (config.exists()) {
SharedPreferences.Editor editor = mm.prefs.edit();
SharedPreferences.Editor editor = App.self.prefs.edit();
try {
SuFileInputStream is = new SuFileInputStream(config);
XmlPullParser parser = Xml.newPullParser();
@ -162,20 +126,20 @@ public class Data {
}
public static void loadConfig() {
MagiskManager mm = MM();
App app = App.self;
// su
suRequestTimeout = Utils.getPrefsInt(mm.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
suResponseType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
suNotificationType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
suRequestTimeout = Utils.getPrefsInt(app.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
suResponseType = Utils.getPrefsInt(app.prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
suNotificationType = Utils.getPrefsInt(app.prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
// config
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
updateChannel = Utils.getPrefsInt(mm.prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
repoOrder = mm.prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_DATE);
isDarkTheme = app.prefs.getBoolean(Const.Key.DARK_THEME, false);
updateChannel = Utils.getPrefsInt(app.prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
repoOrder = app.prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_DATE);
}
public static void writeConfig() {
MM().prefs.edit()
App.self.prefs.edit()
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import android.content.ContentValues;
import android.database.Cursor;

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;

View File

@ -1,10 +1,10 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import android.content.ContentValues;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.core.utils.Utils;
import androidx.annotation.NonNull;

View File

@ -1,12 +1,11 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import android.content.ContentValues;
import android.database.Cursor;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Logger;
import com.topjohnwu.core.utils.Utils;
import java.text.DateFormat;
import java.util.Date;
@ -73,7 +72,7 @@ public class Repo extends BaseModule {
}
public String getDownloadFilename() {
return Download.getLegalFilename(getName() + "-" + getVersion() + ".zip");
return Utils.getLegalFilename(getName() + "-" + getVersion() + ".zip");
}
public class IllegalRepoException extends Exception {

View File

@ -1,8 +1,8 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import android.content.ContentValues;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.core.utils.LocaleManager;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import org.kamranzafar.jtar.TarHeader;

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.container;
package com.topjohnwu.core.container;
import java.util.ArrayList;
import java.util.Collection;

View File

@ -1,16 +1,16 @@
package com.topjohnwu.magisk.database;
package com.topjohnwu.core.database;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.core.container.SuLogEntry;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.text.DateFormat;
@ -23,7 +23,7 @@ import java.util.Map;
public class MagiskDB {
private static final String POLICY_TABLE = "policies";
private static final String LOG_TABLE = "logs";
private static final String LOG_TABLE = "err";
private static final String SETTINGS_TABLE = "settings";
private static final String STRINGS_TABLE = "strings";

View File

@ -1,15 +1,14 @@
package com.topjohnwu.magisk.database;
package com.topjohnwu.core.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Repo;
import java.util.HashSet;
import java.util.Set;
@ -20,12 +19,10 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
private static final String TABLE_NAME = "repos";
private SQLiteDatabase mDb;
private MagiskManager mm;
private ReposAdapter adapter;
private Runnable adapterCb;
public RepoDatabaseHelper(Context context) {
super(context, "repo.db", null, DATABASE_VER);
mm = Data.MM();
mDb = getWritableDatabase();
// Remove outdated repos
@ -46,7 +43,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
App.self.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
}
}
@ -120,17 +117,17 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
return set;
}
public void registerAdapter(ReposAdapter a) {
adapter = a;
public void registerAdapterCallback(Runnable cb) {
adapterCb = cb;
}
public void unregisterAdapter() {
adapter = null;
public void unregisterAdapterCallback() {
adapterCb = null;
}
private void notifyAdapter() {
if (adapter != null) {
Data.mainHandler.post(adapter::notifyDBChanged);
if (adapterCb != null) {
App.mainHandler.post(adapterCb);
}
}
}

View File

@ -1,10 +1,9 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.core.tasks;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
@ -51,7 +50,7 @@ public class CheckUpdates {
url = Const.Url.BETA_URL;
break;
case Const.Value.CUSTOM_CHANNEL:
url = Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
url = App.self.prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
break;
default:
return;
@ -89,15 +88,10 @@ public class CheckUpdates {
JSONObject uninstaller = getJson(json, "uninstaller");
Data.uninstallerLink = getString(uninstaller, "link", null);
if (cb != null) {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
Notifications.managerUpdate();
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
Notifications.magiskUpdate();
}
Topic.publish(Topic.UPDATE_CHECK_DONE);
if (cb != null)
cb.run();
}
Topic.publish(Topic.UPDATE_CHECK_DONE);
}
}
}

View File

@ -0,0 +1,83 @@
package com.topjohnwu.core.tasks;
import android.net.Uri;
import android.os.AsyncTask;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.core.utils.ZipUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public abstract class FlashZip {
private Uri mUri;
private File tmpFile;
private List<String> console, logs;
public FlashZip(Uri uri, List<String> out, List<String> err) {
mUri = uri;
console = out;
logs = err;
tmpFile = new File(App.self.getCacheDir(), "install.zip");
}
private boolean unzipAndCheck() throws IOException {
ZipUtils.unzip(tmpFile, tmpFile.getParentFile(), "META-INF/com/google/android", true);
return ShellUtils.fastCmdResult("grep -q '#MAGISK' " + new File(tmpFile.getParentFile(), "updater-script"));
}
private boolean flash() throws IOException {
console.add("- Copying zip to temp directory");
try (InputStream in = App.self.getContentResolver().openInputStream(mUri);
OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpFile))) {
if (in == null) throw new FileNotFoundException();
InputStream buf= new BufferedInputStream(in);
ShellUtils.pump(buf, out);
} catch (FileNotFoundException e) {
console.add("! Invalid Uri");
throw e;
} catch (IOException e) {
console.add("! Cannot copy to cache");
throw e;
}
try {
if (!unzipAndCheck()) {
console.add("! This zip is not a Magisk Module!");
return false;
}
} catch (IOException e) {
console.add("! Unzip error");
throw e;
}
console.add("- Installing " + Utils.getNameFromUri(App.self, mUri));
return Shell.su("cd " + tmpFile.getParent(),
"BOOTMODE=true sh update-binary dummy 1 " + tmpFile)
.to(console, logs)
.exec().isSuccess();
}
public void exec() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
boolean success = false;
try {
success = flash();
} catch (IOException ignored) {}
Shell.su("cd /", "rm -rf " + tmpFile.getParent() + " " + Const.TMP_FOLDER_PATH).submit();
onResult(success);
});
}
protected abstract void onResult(boolean success);
}

View File

@ -0,0 +1,288 @@
package com.topjohnwu.core.tasks;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.text.TextUtils;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.R;
import com.topjohnwu.core.container.TarEntry;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.net.DownloadProgressListener;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.internal.NOPList;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
import com.topjohnwu.superuser.io.SuFileOutputStream;
import com.topjohnwu.utils.SignBoot;
import org.kamranzafar.jtar.TarInputStream;
import org.kamranzafar.jtar.TarOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import androidx.annotation.MainThread;
import androidx.annotation.WorkerThread;
public abstract class MagiskInstaller {
private List<String> console, logs;
protected String srcBoot;
protected File installDir;
private class ProgressLog implements DownloadProgressListener {
private int prev = -1;
private int location;
@Override
public void onProgress(long bytesDownloaded, long totalBytes) {
if (prev < 0) {
location = console.size();
console.add("... 0%");
}
int curr = (int) (100 * bytesDownloaded / totalBytes);
if (prev != curr) {
prev = curr;
console.set(location, "... " + prev + "%");
}
}
}
protected MagiskInstaller() {
console = NOPList.getInstance();
logs = NOPList.getInstance();
}
public MagiskInstaller(List<String> out, List<String> err) {
console = out;
logs = err;
installDir = new File(Utils.getDEContext().getFilesDir().getParent(), "install");
Shell.sh("rm -rf " + installDir).exec();
installDir.mkdirs();
}
protected boolean extractZip() {
String arch;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
arch = abis.contains("x86") ? "x86" : "arm";
} else {
arch = TextUtils.equals(Build.CPU_ABI, "x86") ? "x86" : "arm";
}
console.add("- Device platform: " + Build.CPU_ABI);
File zip = new File(App.self.getFilesDir(), "magisk.zip");
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
console.add("- Downloading zip");
Networking.get(Data.magiskLink)
.setDownloadProgressListener(new ProgressLog())
.execForFile(zip);
} else {
console.add("- Existing zip found");
}
try {
ZipInputStream zi = new ZipInputStream(new BufferedInputStream(
new FileInputStream(zip), (int) zip.length()));
ZipEntry ze;
while ((ze = zi.getNextEntry()) != null) {
if (ze.isDirectory())
continue;
String name = null;
String[] names = { arch + "/", "common/", "META-INF/com/google/android/update-binary" };
for (String n : names) {
if (ze.getName().startsWith(n)) {
name = ze.getName().substring(ze.getName().lastIndexOf('/') + 1);
break;
}
}
if (name == null && ze.getName().startsWith("chromeos/"))
name = ze.getName();
if (name == null)
continue;
File dest;
if (installDir instanceof SuFile) {
dest = new SuFile(installDir, name);
} else {
dest = new File(installDir, name);
}
dest.getParentFile().mkdirs();
try (OutputStream out = new SuFileOutputStream(dest)) {
ShellUtils.pump(zi, out);
}
}
} catch (IOException e) {
console.add("! Cannot unzip zip");
return false;
}
Shell.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
installDir, installDir, installDir)).exec();
return true;
}
protected boolean copyBoot(Uri bootUri) {
srcBoot = new File(installDir, "boot.img").getPath();
console.add("- Copying image to cache");
// Copy boot image to local
try (InputStream in = App.self.getContentResolver().openInputStream(bootUri);
OutputStream out = new FileOutputStream(srcBoot)) {
if (in == null)
throw new FileNotFoundException();
InputStream src;
if (Utils.getNameFromUri(App.self, bootUri).endsWith(".tar")) {
// Extract boot.img from tar
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
org.kamranzafar.jtar.TarEntry entry;
while ((entry = tar.getNextEntry()) != null) {
if (entry.getName().equals("boot.img"))
break;
}
src = tar;
} else {
// Direct copy raw image
src = new BufferedInputStream(in);
}
ShellUtils.pump(src, out);
} catch (FileNotFoundException e) {
console.add("! Invalid Uri");
return false;
} catch (IOException e) {
console.add("! Copy failed");
return false;
}
return true;
}
protected boolean patchBoot() {
boolean isSigned;
try (InputStream in = new SuFileInputStream(srcBoot)) {
isSigned = SignBoot.verifySignature(in, null);
if (isSigned) {
console.add("- Boot image is signed with AVB 1.0");
}
} catch (IOException e) {
console.add("! Unable to check signature");
return false;
}
// Patch boot image
if (!Shell.sh(Utils.fmt("cd %s; KEEPFORCEENCRYPT=%b KEEPVERITY=%b " +
"sh update-binary indep boot_patch.sh %s",
installDir, Data.keepEnc, Data.keepVerity, srcBoot))
.to(console, logs).exec().isSuccess())
return false;
Shell.Job job = Shell.sh("mv bin/busybox busybox",
"rm -rf magisk.apk bin boot.img update-binary",
"cd /");
File patched = new File(installDir, "new-boot.img");
if (isSigned) {
console.add("- Signing boot image with test keys");
File signed = new File(installDir, "signed.img");
try (InputStream in = new SuFileInputStream(patched);
OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))) {
SignBoot.doSignature("/boot", in, out, null, null);
} catch (IOException e) {
return false;
}
job.add("mv -f " + signed + " " + patched);
}
job.exec();
return true;
}
protected boolean flashBoot() {
if (!Shell.su(Utils.fmt("direct_install %s %s", installDir, srcBoot))
.to(console, logs).exec().isSuccess())
return false;
if (!Data.keepVerity)
Shell.su("find_dtbo_image", "patch_dtbo_image").to(console, logs).exec();
return true;
}
protected boolean storeBoot() {
File patched = new File(installDir, "new-boot.img");
String fmt = App.self.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + fmt);
dest.getParentFile().mkdirs();
OutputStream os;
try {
switch (fmt) {
case ".img.tar":
os = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
((TarOutputStream) os).putNextEntry(new TarEntry(patched, "boot.img"));
break;
default:
case ".img":
os = new BufferedOutputStream(new FileOutputStream(dest));
break;
}
try (InputStream in = new SuFileInputStream(patched)) {
ShellUtils.pump(in, os);
os.close();
}
} catch (IOException e) {
console.add("! Failed to store boot to " + dest);
return false;
}
Shell.sh("rm -f " + patched).exec();
console.add("");
console.add("****************************");
console.add(" Patched image is placed in ");
console.add(" " + dest + " ");
console.add("****************************");
return true;
}
protected boolean postOTA() {
SuFile bootctl = new SuFile(Const.MAGISK_PATH + "/.core/bootctl");
try (InputStream in = App.self.getResources().openRawResource(R.raw.bootctl);
OutputStream out = new SuFileOutputStream(bootctl)) {
ShellUtils.pump(in, out);
} catch (IOException e) {
e.printStackTrace();
return false;
}
Shell.su("post_ota " + bootctl.getParent()).exec();
console.add("***************************************");
console.add(" Next reboot will boot to second slot!");
console.add("***************************************");
return true;
}
@WorkerThread
protected abstract boolean operations();
@MainThread
protected abstract void onResult(boolean success);
public void exec() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
boolean b = operations();
App.mainHandler.post(() -> onResult(b));
});
}
}

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.core.tasks;
import android.app.Activity;
import android.os.AsyncTask;

View File

@ -1,11 +1,11 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.core.tasks;
import android.app.Activity;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.ISafetyNetHelper;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
@ -16,7 +16,7 @@ import dalvik.system.DexClassLoader;
public class SafetyNet {
public static final File EXT_APK =
new File(Data.MM().getFilesDir().getParent() + "/snet", "snet.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(),

View File

@ -1,15 +1,14 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.core.tasks;
import android.database.Cursor;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.container.Repo;
import com.topjohnwu.core.utils.Logger;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.Request;
@ -35,7 +34,7 @@ public class UpdateRepos {
private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
private static final DateFormat dateFormat;
private MagiskManager mm;
private App app = App.self;
private Set<String> cached;
private ExecutorService threadPool;
@ -44,10 +43,6 @@ public class UpdateRepos {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public UpdateRepos() {
mm = Data.MM();
}
private void waitTasks() {
threadPool.shutdown();
while (true) {
@ -64,17 +59,17 @@ public class UpdateRepos {
String id = rawRepo.getString("name");
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
threadPool.execute(() -> {
Repo repo = mm.repoDB.getRepo(id);
Repo repo = app.repoDB.getRepo(id);
try {
if (repo == null)
repo = new Repo(id);
else
cached.remove(id);
repo.update(date);
mm.repoDB.addRepo(repo);
app.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
Logger.debug(e.getMessage());
mm.repoDB.removeRepo(id);
app.repoDB.removeRepo(id);
}
});
}
@ -86,7 +81,7 @@ public class UpdateRepos {
private boolean loadPage(int page) {
Request req = Networking.get(Utils.fmt(Const.Url.REPO_URL, page + 1));
if (page == 0) {
String etag = mm.prefs.getString(Const.Key.ETAG_KEY, null);
String etag = app.prefs.getString(Const.Key.ETAG_KEY, null);
if (etag != null)
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
}
@ -115,7 +110,7 @@ public class UpdateRepos {
String etag = res.getConnection().getHeaderField(Const.Key.ETAG_KEY);
if (etag != null) {
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
app.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
}
}
@ -128,16 +123,16 @@ public class UpdateRepos {
}
private void fullReload() {
Cursor c = mm.repoDB.getRawCursor();
Cursor c = app.repoDB.getRawCursor();
while (c.moveToNext()) {
Repo repo = new Repo(c);
threadPool.execute(() -> {
try {
repo.update();
mm.repoDB.addRepo(repo);
app.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
Logger.debug(e.getMessage());
mm.repoDB.removeRepo(repo);
app.repoDB.removeRepo(repo);
}
});
}
@ -147,13 +142,13 @@ public class UpdateRepos {
public void exec(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
cached = Collections.synchronizedSet(app.repoDB.getRepoIDSet());
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
if (loadPages()) {
waitTasks();
// The leftover cached means they are removed from online repo
mm.repoDB.removeRepo(cached);
app.repoDB.removeRepo(cached);
} else if (force) {
fullReload();
}

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import com.topjohnwu.utils.SignBoot;

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
public interface ISafetyNetHelper {

View File

@ -1,13 +1,11 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import java.util.ArrayList;
import java.util.Collections;
@ -22,15 +20,15 @@ public class LocaleManager {
public final static Locale defaultLocale = Locale.getDefault();
public static List<Locale> locales;
public static void setLocale(MagiskManager mm) {
String localeConfig = mm.prefs.getString(Const.Key.LOCALE, "");
public static void setLocale(App app) {
String localeConfig = app.prefs.getString(Const.Key.LOCALE, "");
if (localeConfig.isEmpty()) {
locale = defaultLocale;
} else {
locale = Locale.forLanguageTag(localeConfig);
}
Locale.setDefault(locale);
Resources res = mm.getResources();
Resources res = app.getResources();
Configuration config = res.getConfiguration();
config.setLocale(locale);
res.updateConfiguration(config, res.getDisplayMetrics());
@ -39,18 +37,16 @@ public class LocaleManager {
public static String getString(Locale locale, @StringRes int id) {
Configuration config = new Configuration();
config.setLocale(locale);
return Data.MM().createConfigurationContext(config).getString(id);
return App.self.createConfigurationContext(config).getString(id);
}
public static void loadAvailableLocales() {
public static void loadAvailableLocales(@StringRes int compareId) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
locales = new ArrayList<>();
HashSet<String> set = new HashSet<>();
Resources res = Data.MM().getResources();
Resources res = App.self.getResources();
Locale locale;
@StringRes int compareId = R.string.download_file_error;
// Add default locale
locales.add(Locale.ENGLISH);
set.add(getString(Locale.ENGLISH, compareId));

View File

@ -1,9 +1,9 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import android.util.Log;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.core.BuildConfig;
import com.topjohnwu.core.Const;
public class Logger {

View File

@ -1,10 +1,10 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import android.content.Context;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.R;
import com.topjohnwu.superuser.BusyBox;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;

View File

@ -1,6 +1,6 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.App;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -67,7 +67,7 @@ public class Topic {
topicList[topic].published = true;
}
for (Subscriber sub : topicList[topic].subscribers) {
Data.mainHandler.post(() -> sub.onPublish(topic, results));
App.mainHandler.post(() -> sub.onPublish(topic, results));
}
}

View File

@ -1,10 +1,6 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@ -13,16 +9,15 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.provider.OpenableColumns;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.container.ValueSortedMap;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
@ -32,6 +27,19 @@ import java.util.Map;
public class Utils {
public static void toast(CharSequence msg, int duration) {
App.mainHandler.post(() -> Toast.makeText(App.self, msg, duration).show());
}
public static void toast(int resId, int duration) {
App.mainHandler.post(() -> Toast.makeText(App.self, resId, duration).show());
}
public static String dlString(String url) {
String s = Networking.get(url).execForString().getResult();
return s == null ? "" : s;
}
public static int getPrefsInt(SharedPreferences prefs, String key, int def) {
return Integer.parseInt(prefs.getString(key, String.valueOf(def)));
}
@ -59,7 +67,7 @@ public class Utils {
}
public static int dpInPx(int dp) {
float scale = Data.MM().getResources().getDisplayMetrics().density;
float scale = App.self.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5);
}
@ -67,51 +75,23 @@ public class Utils {
return String.format(Locale.US, fmt, args);
}
public static String dos2unix(String s) {
String newString = s.replace("\r\n", "\n");
if(!newString.endsWith("\n")) {
return newString + "\n";
} else {
return newString;
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
try {
if (info.labelRes > 0) {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
}
} catch (Exception ignored) {}
return info.loadLabel(pm).toString();
}
public static void setupUpdateCheck() {
MagiskManager mm = Data.MM();
JobScheduler scheduler = (JobScheduler) mm.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(mm, Data.classMap.get(UpdateCheckService.class));
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(8 * 60 * 60 * 1000)
.build();
scheduler.schedule(info);
}
} else {
scheduler.cancel(Const.UPDATE_SERVICE_VER);
}
}
public static void openLink(Context context, Uri link) {
Intent intent = new Intent(Intent.ACTION_VIEW, link);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
} else {
toast(R.string.open_link_failed_toast, Toast.LENGTH_SHORT);
}
}
public static void toast(CharSequence msg, int duration) {
Data.mainHandler.post(() -> Toast.makeText(Data.MM(), msg, duration).show());
}
public static void toast(int resId, int duration) {
Data.mainHandler.post(() -> Toast.makeText(Data.MM(), resId, duration).show());
public static String getLegalFilename(CharSequence filename) {
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
.replace("$", "").replace("`", "").replace("*", "").replace("/", "_")
.replace("#", "").replace("@", "").replace("\\", "_");
}
public static void loadModules() {
@ -130,29 +110,16 @@ public class Utils {
});
}
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
try {
if (info.labelRes > 0) {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
}
} catch (Exception ignored) {}
return info.loadLabel(pm).toString();
}
public static boolean showSuperUser() {
if (Data.multiuserState < 0)
Data.multiuserState = Data.MM().mDB.getSettings(Const.Key.SU_MULTIUSER_MODE,
Data.multiuserState = App.self.mDB.getSettings(Const.Key.SU_MULTIUSER_MODE,
Const.Value.MULTIUSER_MODE_OWNER_ONLY);
return Shell.rootAccess() && (Const.USER_ID == 0 ||
Data.multiuserState != Const.Value.MULTIUSER_MODE_OWNER_MANAGED);
}
public static String dlString(String url) {
String s = Networking.get(url).execForString().getResult();
return s == null ? "" : s;
public static Context getDEContext() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
App.self.createDeviceProtectedStorageContext() : App.self;
}
}

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.core.utils;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;

View File

@ -63,9 +63,9 @@ restore_imgs() {
post_ota() {
cd $1
chmod 755 bootctl
./bootctl hal-info || return
[ `./bootctl get-current-slot` -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
./bootctl set-active-boot-slot $SLOT_NUM
../../../../../app/src/full/res/raw/bootctl hal-info || return
[ `../../../../../app/src/full/res/raw/bootctl get-current-slot` -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
../../../../../app/src/full/res/raw/bootctl set-active-boot-slot $SLOT_NUM
echo '${0%/*}/../bootctl mark-boot-successful;rm -f ${0%/*}/../bootctl $0' > post-fs-data.d/post_ota.sh
chmod 755 post-fs-data.d/post_ota.sh
cd /

View File

@ -1 +1 @@
include ':app', ':native', ':utils', ':snet', ':net'
include ':app', ':native', ':utils', ':snet', ':net', ':core'