Update 0.2.0-pre1

pull/27/head 0.2.0-pre1
Fox2Code 4 years ago
parent 1cd81e1cad
commit 88da4ba372

@ -10,8 +10,8 @@ android {
applicationId "com.fox2code.mmm"
minSdk 21
targetSdk 30
versionCode 6
versionName "0.1.2"
versionCode 7
versionName "0.2.0-pre1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -51,7 +51,7 @@ dependencies {
// UI
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.google.android.material:material:1.4.0'

@ -36,7 +36,8 @@ public enum ActionButtonType {
@Override
public void update(ImageButton button, ModuleHolder moduleHolder) {
int icon = moduleHolder.hasUpdate() ?
R.drawable.ic_baseline_update_24 : R.drawable.ic_baseline_download_24;
R.drawable.ic_baseline_update_24 :
R.drawable.ic_baseline_system_update_24;
button.setImageResource(icon);
}

@ -0,0 +1,104 @@
package com.fox2code.mmm;
import android.util.Log;
import com.fox2code.mmm.utils.Http;
import org.json.JSONArray;
import org.json.JSONObject;
import java.nio.charset.StandardCharsets;
// See https://docs.github.com/en/rest/reference/repos#releases
public class AppUpdateManager {
private static final String TAG = "AppUpdateManager";
private static final AppUpdateManager INSTANCE = new AppUpdateManager();
private static final String RELEASES_API_URL =
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
public static AppUpdateManager getAppUpdateManager() {
return INSTANCE;
}
private final Object updateLock = new Object();
private String latestRelease;
private String latestPreRelease;
private long lastChecked;
private boolean preReleaseNewer;
private AppUpdateManager() {
this.latestRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_release", BuildConfig.VERSION_NAME);
this.latestPreRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_pre_release", BuildConfig.VERSION_NAME);
this.lastChecked = 0;
this.preReleaseNewer = true;
}
// Return true if should show a notification
public boolean checkUpdate(boolean force) {
if (this.peekShouldUpdate())
return true;
long lastChecked = this.lastChecked;
if (!force && lastChecked != 0 &&
// Avoid spam calls by putting a 10 seconds timer
lastChecked < System.currentTimeMillis() - 10000L)
return false;
synchronized (this.updateLock) {
if (lastChecked != this.lastChecked)
return this.peekShouldUpdate();
boolean preReleaseNewer = true;
try {
JSONArray releases = new JSONArray(new String(Http.doHttpGet(
RELEASES_API_URL, false), StandardCharsets.UTF_8));
String latestRelease = null, latestPreRelease = null;
for (int i = releases.length() - 1; i > 0; i--) {
JSONObject release = releases.getJSONObject(i);
// Skip invalid entries
if (release.getBoolean("draft")) continue;
boolean preRelease = release.getBoolean("prerelease");
String version = release.getString("tag_name");
if (version.startsWith("v"))
version = version.substring(1);
if (preRelease) {
latestPreRelease = version;
} else {
latestRelease = version;
if (latestPreRelease == null)
preReleaseNewer = false;
}
if (latestRelease != null && latestPreRelease != null) {
break; // We read everything we needed to read.
}
}
if (latestRelease != null)
this.latestRelease = latestRelease;
if (latestPreRelease != null) {
this.latestPreRelease = latestPreRelease;
this.preReleaseNewer = preReleaseNewer;
} else if (!preReleaseNewer) {
this.latestPreRelease = "";
this.preReleaseNewer = false;
}
Log.d(TAG, "Latest release: " + latestRelease);
Log.d(TAG, "Latest pre-release: " + latestPreRelease);
Log.d(TAG, "Latest pre-release newer: " + preReleaseNewer);
this.lastChecked = System.currentTimeMillis();
} catch (Exception ioe) {
Log.e("AppUpdateManager", "Failed to check releases", ioe);
}
}
return this.peekShouldUpdate();
}
public boolean peekShouldUpdate() {
return !(BuildConfig.VERSION_NAME.equals(this.latestRelease) ||
(this.preReleaseNewer &&
BuildConfig.VERSION_NAME.equals(this.latestPreRelease)));
}
public boolean peekHasUpdate() {
return !BuildConfig.VERSION_NAME.equals(this.preReleaseNewer ?
this.latestPreRelease : this.latestRelease);
}
}

@ -123,6 +123,8 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
});
if (!RepoManager.getINSTANCE().hasConnectivity())
moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET);
else if (AppUpdateManager.getAppUpdateManager().checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
moduleViewListBuilder.appendRemoteModules();
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);
Log.i(TAG, "Finished app opening state!");
@ -180,6 +182,8 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
moduleViewListBuilder.addNotification(NotificationType.SHOWCASE_MODE);
if (!RepoManager.getINSTANCE().hasConnectivity())
moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET);
else if (AppUpdateManager.getAppUpdateManager().checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
moduleViewListBuilder.appendRemoteModules();
Log.i(TAG, "Common Before applyTo");
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);

@ -15,6 +15,7 @@ import androidx.annotation.StyleRes;
import androidx.appcompat.view.ContextThemeWrapper;
import com.fox2code.mmm.compat.CompatActivity;
import com.fox2code.mmm.compat.CompatThemeWrapper;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.GMSProviderInstaller;
import com.fox2code.mmm.utils.Http;
@ -84,6 +85,10 @@ public class MainApplication extends Application implements CompatActivity.Appli
return getSharedPreferences().getBoolean("pref_show_incompatible", false);
}
public static boolean isForceEnglish() {
return getSharedPreferences().getBoolean("pref_force_english", false);
}
public static boolean isForceDarkTerminal() {
return getSharedPreferences().getBoolean("pref_force_dark_terminal", false);
}
@ -137,16 +142,19 @@ public class MainApplication extends Application implements CompatActivity.Appli
@StyleRes
private int managerThemeResId = R.style.Theme_MagiskModuleManager;
private ContextThemeWrapper markwonThemeContext;
private Boolean nightModeOverride = null;
private CompatThemeWrapper markwonThemeContext;
private Markwon markwon;
public Markwon getMarkwon() {
if (this.markwon != null)
return this.markwon;
ContextThemeWrapper contextThemeWrapper = this.markwonThemeContext;
if (contextThemeWrapper == null)
CompatThemeWrapper contextThemeWrapper = this.markwonThemeContext;
if (contextThemeWrapper == null) {
contextThemeWrapper = this.markwonThemeContext =
new ContextThemeWrapper(this, this.managerThemeResId);
new CompatThemeWrapper(this, this.managerThemeResId);
contextThemeWrapper.setForceEnglish(isForceEnglish());
}
Markwon markwon = Markwon.builder(contextThemeWrapper).usePlugin(HtmlPlugin.create())
.usePlugin(SyntaxHighlightPlugin.create(
new Prism4j(new Prism4jGrammarLocator()), new Prism4jSwitchTheme()))
@ -155,6 +163,10 @@ public class MainApplication extends Application implements CompatActivity.Appli
return this.markwon = markwon;
}
public CompatThemeWrapper getMarkwonThemeContext() {
return markwonThemeContext;
}
private class Prism4jSwitchTheme implements Prism4jTheme {
private final Prism4jTheme light = new Prism4jThemeDefault(Color.TRANSPARENT);
private final Prism4jTheme dark = new Prism4jThemeDarkula(Color.TRANSPARENT);
@ -180,10 +192,22 @@ public class MainApplication extends Application implements CompatActivity.Appli
}
}
@SuppressLint("NonConstantResourceId")
public void setManagerThemeResId(@StyleRes int resId) {
this.managerThemeResId = resId;
if (this.markwonThemeContext != null)
switch (this.managerThemeResId) {
case R.style.Theme_MagiskModuleManager:
this.nightModeOverride = null;
case R.style.Theme_MagiskModuleManager_Light:
this.nightModeOverride = Boolean.FALSE;
case R.style.Theme_MagiskModuleManager_Dark:
this.nightModeOverride = Boolean.TRUE;
default:
}
if (this.markwonThemeContext != null) {
this.markwonThemeContext.setNightModeOverride(this.nightModeOverride);
this.markwonThemeContext.setTheme(resId);
}
this.markwon = null;
}
@ -250,11 +274,15 @@ public class MainApplication extends Application implements CompatActivity.Appli
@Override
public void onCreateCompatActivity(CompatActivity compatActivity) {
compatActivity.setNightModeOverride(this.nightModeOverride);
compatActivity.setForceEnglish(isForceEnglish());
compatActivity.setTheme(this.managerThemeResId);
}
@Override
public void onRefreshUI(CompatActivity compatActivity) {
compatActivity.setNightModeOverride(this.nightModeOverride);
compatActivity.setForceEnglish(isForceEnglish());
compatActivity.setThemeRecreate(this.managerThemeResId);
}

@ -83,6 +83,10 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
return timeStamp <= 0 ? "" :
MainApplication.formatTime(timeStamp);
}
public String getRepoName() {
if (this.repoModule == null) return "";
return this.repoModule.repoName;
}
public boolean hasFlag(int flag) {
return this.moduleInfo != null && this.moduleInfo.hasFlag(flag);

@ -15,11 +15,13 @@ import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.manager.ModuleManager;
import com.fox2code.mmm.repo.RepoModule;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.topjohnwu.superuser.internal.UiThreadHandler;
@ -142,6 +144,11 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter<ModuleViewAdap
this.initState = false;
}
@NonNull
public final String getString(@StringRes int resId) {
return this.itemView.getContext().getString(resId);
}
@SuppressLint("SetTextI18n")
public boolean update(ModuleHolder moduleHolder) {
this.initState = true;
@ -170,16 +177,21 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter<ModuleViewAdap
ModuleInfo moduleInfo = moduleHolder.getMainModuleInfo();
this.titleText.setText(moduleInfo.name);
this.creditText.setText(moduleInfo.version + " by " + moduleInfo.author);
this.creditText.setText(moduleInfo.version + " " +
this.getString(R.string.module_by) + " " + moduleInfo.author);
this.descriptionText.setText(moduleInfo.description);
String updateText = moduleHolder.getUpdateTimeText();
if (!updateText.isEmpty()) {
this.updateText.setVisibility(View.VISIBLE);
this.updateText.setText(this.updateText.getContext()
.getString(R.string.last_updated) + " " + updateText);
this.updateText.setText(
this.getString(R.string.module_last_update) + " " + updateText + "\n" +
this.getString(R.string.module_repo) + " " + moduleHolder.getRepoName());
} else if (moduleHolder.moduleId.equals("hosts")) {
this.updateText.setVisibility(View.VISIBLE);
this.updateText.setText(R.string.magisk_builtin_module);
} else if (moduleHolder.moduleId.equals("substratum")) {
this.updateText.setVisibility(View.VISIBLE);
this.updateText.setText(R.string.substratum_builtin_module);
} else {
this.updateText.setVisibility(View.GONE);
}

@ -24,7 +24,7 @@ interface NotificationTypeCst {
}
public enum NotificationType implements NotificationTypeCst {
SHOWCASE_MODE(R.string.showcase_mode, R.drawable.ic_baseline_monitor_24,
SHOWCASE_MODE(R.string.showcase_mode, R.drawable.ic_baseline_lock_24,
R.attr.colorPrimary, R.attr.colorOnPrimary) {
@Override
public boolean shouldRemove() {
@ -51,6 +51,16 @@ public enum NotificationType implements NotificationTypeCst {
return RepoManager.getINSTANCE().hasConnectivity();
}
},
UPDATE_AVAILABLE(R.string.app_update_available, R.drawable.ic_baseline_system_update_24,
R.attr.colorPrimary, R.attr.colorOnPrimary, v -> {
IntentHelper.openUrl(v.getContext(),
"https://github.com/Fox2Code/FoxMagiskModuleManager/releases");
}, false) {
@Override
public boolean shouldRemove() {
return !AppUpdateManager.getAppUpdateManager().peekShouldUpdate();
}
},
INSTALL_FROM_STORAGE(R.string.install_from_storage, R.drawable.ic_baseline_storage_24,
R.attr.colorBackgroundFloating, R.attr.colorOnBackground, v -> {
CompatActivity compatActivity = CompatActivity.getCompatActivity(v);

@ -21,10 +21,11 @@ import androidx.fragment.app.Fragment;
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.R;
import java.util.Locale;
import java.util.Objects;
/**
* I will probably outsource this to a separate library
* I will probably outsource this to a separate library later
*/
public class CompatActivity extends AppCompatActivity {
public static final int INTENT_ACTIVITY_REQUEST_CODE = 0x01000000;
@ -38,6 +39,7 @@ public class CompatActivity extends AppCompatActivity {
}
};
private final CompatConfigHelper compatConfigHelper = new CompatConfigHelper(this);
private CompatActivity.OnActivityResultCallback onActivityResultCallback;
private CompatActivity.OnBackPressedCallback onBackPressedCallback;
private MenuItem.OnMenuItemClickListener menuClickListener;
@ -45,7 +47,10 @@ public class CompatActivity extends AppCompatActivity {
private boolean onCreateCalled = false;
private boolean isRefreshUi = false;
private int drawableResId;
MenuItem menuItem;
private MenuItem menuItem;
// CompatConfigHelper
private boolean forceEnglish;
private Boolean nightModeOverride;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@ -55,6 +60,8 @@ public class CompatActivity extends AppCompatActivity {
}
super.onCreate(savedInstanceState);
this.onCreateCalled = true;
this.checkResourcesOverrides(
this.forceEnglish, this.nightModeOverride);
}
@Override
@ -88,6 +95,8 @@ public class CompatActivity extends AppCompatActivity {
} finally {
this.isRefreshUi = false;
}
this.checkResourcesOverrides(
this.forceEnglish, this.nightModeOverride);
}
}
@ -128,6 +137,7 @@ public class CompatActivity extends AppCompatActivity {
this.menuItem.setOnMenuItemClickListener(this.menuClickListener);
this.menuItem.setIcon(this.drawableResId);
this.menuItem.setEnabled(true);
this.menuItem.setVisible(true);
}
}
@ -138,6 +148,7 @@ public class CompatActivity extends AppCompatActivity {
this.menuItem.setOnMenuItemClickListener(null);
this.menuItem.setIcon(null);
this.menuItem.setEnabled(false);
this.menuItem.setVisible(false);
}
}
@ -161,12 +172,16 @@ public class CompatActivity extends AppCompatActivity {
@Override
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
super.onApplyThemeResource(theme, resid, first);
if (resid != 0 && this.setThemeDynamic == resid) {
super.onApplyThemeResource(theme, resid, first);
Activity parent = this.getParent();
(parent == null ? this : parent).recreate();
super.overridePendingTransition(
android.R.anim.fade_in, android.R.anim.fade_out);
} else {
this.compatConfigHelper.checkResourcesOverrides(theme,
this.forceEnglish, this.nightModeOverride);
super.onApplyThemeResource(theme, resid, first);
}
}
@ -199,6 +214,7 @@ public class CompatActivity extends AppCompatActivity {
this.menuItem.setOnMenuItemClickListener(this.menuClickListener);
this.menuItem.setIcon(this.drawableResId);
this.menuItem.setEnabled(true);
this.menuItem.setVisible(true);
}
return super.onCreateOptionsMenu(menu);
}
@ -224,6 +240,27 @@ public class CompatActivity extends AppCompatActivity {
}
}
public void setForceEnglish(boolean forceEnglish) {
if (this.forceEnglish == forceEnglish) return;
this.forceEnglish = forceEnglish;
this.checkResourcesOverrides(forceEnglish, this.nightModeOverride);
}
public void setNightModeOverride(Boolean nightModeOverride) {
if (this.nightModeOverride == nightModeOverride) return;
this.nightModeOverride = nightModeOverride;
this.checkResourcesOverrides(this.forceEnglish, nightModeOverride);
}
private void checkResourcesOverrides(boolean forceEnglish,Boolean nightModeOverride) {
if (this.isRefreshUi || !this.onCreateCalled) return; // Wait before reload
this.compatConfigHelper.checkResourcesOverrides(forceEnglish, nightModeOverride);
}
public Locale getUserLocale() {
return this.compatConfigHelper.getUserLocale();
}
public static CompatActivity getCompatActivity(View view) {
return getCompatActivity(view.getContext());
}

@ -0,0 +1,76 @@
package com.fox2code.mmm.compat;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
import java.util.Locale;
/**
* I will probably outsource this to a separate library later
*/
final class CompatConfigHelper {
// ENGLISH like this is an unnatural local, as it doesn't precise the country
// All english locales settable by the user precise the country (Ex: en-US)
private static final Locale english = Locale.ENGLISH;
private final Context context;
private Locale userLocale;
CompatConfigHelper(Context context) {
this.context = context;
}
void checkResourcesOverrides(boolean forceEnglish,
Boolean nightModeOverride) {
this.checkResourcesOverrides(
this.context.getTheme(),
forceEnglish, nightModeOverride);
}
void checkResourcesOverrides(Resources.Theme theme, boolean forceEnglish,
Boolean nightModeOverride) {
final Resources res = theme.getResources();
final Configuration conf = res.getConfiguration();
Locale current = conf.locale;
boolean didChange = false;
if (forceEnglish != current.equals(english)) {
didChange = true;
if (forceEnglish) {
this.userLocale = conf.locale;
conf.locale = english;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
conf.setLocales(LocaleList.getEmptyLocaleList());
}
} else {
conf.locale = this.userLocale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
conf.setLocales(LocaleList.getAdjustedDefault());
}
}
}
int nightMode = conf.uiMode & Configuration.UI_MODE_NIGHT_MASK;
int sysNightMode = Resources.getSystem()
.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (nightModeOverride == null ? sysNightMode != nightMode :
nightMode != (nightModeOverride ?
Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO)) {
didChange = true;
nightMode = nightModeOverride == null ? sysNightMode : nightModeOverride ?
Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO;
conf.uiMode = nightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
}
if (didChange) {
res.updateConfiguration(conf, null);
if (!forceEnglish) this.userLocale = null;
}
}
public Locale getUserLocale() {
// Only use cached value if force english
Locale locale = this.context.getResources().getConfiguration().locale;
return english.equals(locale) ? this.userLocale : locale;
}
}

@ -0,0 +1,56 @@
package com.fox2code.mmm.compat;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import androidx.annotation.StyleRes;
import androidx.appcompat.view.ContextThemeWrapper;
/**
* I will probably outsource this to a separate library later
*/
public class CompatThemeWrapper extends ContextThemeWrapper {
private final CompatConfigHelper compatConfigHelper = new CompatConfigHelper(this);
private boolean canReload;
// CompatConfigHelper
private boolean forceEnglish;
private Boolean nightModeOverride;
public CompatThemeWrapper(Context base, @StyleRes int themeResId) {
super(base, themeResId);
this.canReload = true;
this.checkResourcesOverrides(
this.forceEnglish, this.nightModeOverride);
}
@Override
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
boolean couldReload = this.canReload;
if (couldReload) this.canReload = false;
this.compatConfigHelper.checkResourcesOverrides(theme,
this.forceEnglish, this.nightModeOverride);
super.onApplyThemeResource(theme, resid, first);
if (couldReload) this.canReload = true;
// In case value change while reload, should have no effect
this.compatConfigHelper.checkResourcesOverrides(theme,
this.forceEnglish, this.nightModeOverride);
}
public void setForceEnglish(boolean forceEnglish) {
if (this.forceEnglish == forceEnglish) return;
this.forceEnglish = forceEnglish;
this.checkResourcesOverrides(forceEnglish, this.nightModeOverride);
}
public void setNightModeOverride(Boolean nightModeOverride) {
if (this.nightModeOverride == nightModeOverride) return;
this.nightModeOverride = nightModeOverride;
this.checkResourcesOverrides(this.forceEnglish, nightModeOverride);
}
private void checkResourcesOverrides(boolean forceEnglish,Boolean nightModeOverride) {
if (!this.canReload) return; // Do not reload during theme reload
this.compatConfigHelper.checkResourcesOverrides(forceEnglish, nightModeOverride);
}
}

@ -54,7 +54,9 @@ public class RepoData {
List<RepoModule> populate(JSONObject jsonObject) throws JSONException {
List<RepoModule> newModules = new ArrayList<>();
synchronized (this.populateLock) {
String name = jsonObject.getString("name");
String name = jsonObject.getString("name").trim();
String nameForModules = name.endsWith(" (Official)") ?
name.substring(0, name.length() - 11) : name;
long lastUpdate = jsonObject.getLong("last_update");
for (RepoModule repoModule : this.moduleHashMap.values()) {
repoModule.processed = false;
@ -80,6 +82,7 @@ public class RepoData {
}
}
repoModule.processed = true;
repoModule.repoName = nameForModules;
repoModule.lastUpdated = moduleLastUpdate;
repoModule.notesUrl = moduleNotesUrl;
repoModule.propUrl = modulePropsUrl;

@ -5,6 +5,7 @@ import com.fox2code.mmm.manager.ModuleInfo;
public class RepoModule {
public final ModuleInfo moduleInfo;
public final String id;
public String repoName;
public long lastUpdated;
public String propUrl;
public String zipUrl;

@ -9,12 +9,15 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import com.fox2code.mmm.AppUpdateManager;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
import com.fox2code.mmm.compat.CompatActivity;
import com.fox2code.mmm.compat.CompatThemeWrapper;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.repo.RepoData;
import com.fox2code.mmm.repo.RepoManager;
@ -31,8 +34,8 @@ public class SettingsActivity extends CompatActivity {
super.onCreate(savedInstanceState);
this.setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.settings_activity);
setTitle(R.string.app_name);
if (savedInstanceState == null) {
setTitle(R.string.app_name);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.settings, new SettingsFragment())
@ -73,6 +76,16 @@ public class SettingsActivity extends CompatActivity {
CompatActivity.getCompatActivity(this).setThemeRecreate(themeResId);
return true;
});
Preference forceEnglish = findPreference("pref_force_english");
forceEnglish.setOnPreferenceChangeListener((preference, newValue) -> {
CompatThemeWrapper compatThemeWrapper =
MainApplication.getINSTANCE().getMarkwonThemeContext();
if (compatThemeWrapper != null) {
compatThemeWrapper.setForceEnglish(
Boolean.parseBoolean(String.valueOf(newValue)));
}
return true;
});
if ("dark".equals(themePreference.getValue())) {
findPreference("pref_force_dark_terminal").setEnabled(false);
}
@ -88,6 +101,14 @@ public class SettingsActivity extends CompatActivity {
final LibsBuilder libsBuilder = new LibsBuilder()
.withFields(R.string.class.getFields()).withShowLoadingProgress(false)
.withLicenseShown(true).withAboutMinimalDesign(false);
Preference update = findPreference("pref_update");
update.setVisible(AppUpdateManager.getAppUpdateManager().peekHasUpdate());
update.setOnPreferenceClickListener(p -> {
devModeStep = 0;
IntentHelper.openUrl(p.getContext(),
"https://github.com/Fox2Code/FoxMagiskModuleManager/releases");
return true;
});
findPreference("pref_source_code").setOnPreferenceClickListener(p -> {
if (devModeStep == 2 && (BuildConfig.DEBUG || !MainApplication.isDeveloper())) {
devModeStep = 0;
@ -97,7 +118,8 @@ public class SettingsActivity extends CompatActivity {
R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show();
return true;
}
IntentHelper.openUrl(p.getContext(), "https://github.com/Fox2Code/FoxMagiskModuleManager");
IntentHelper.openUrl(p.getContext(),
"https://github.com/Fox2Code/FoxMagiskModuleManager");
return true;
});
findPreference("pref_show_licenses").setOnPreferenceClickListener(p -> {

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM16,13h-3L13,8h-2v5L8,13l4,4 4,-4z"/>
</vector>

@ -2,6 +2,7 @@
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/compat_menu_item"
android:visible="false"
android:enabled="false"
android:icon="@null"
android:title=""

@ -12,6 +12,9 @@
<string name="fail_internet">無法連線到網路</string>
<string name="title_activity_settings">設定活動</string>
<!-- Module section translation -->
<string name="module_last_update">最後更新:</string>
<!-- Preference Titles -->
<string name="showcase_mode_pref">鎖定模式</string>
<string name="showcase_mode_desc">鎖定模式阻止管理器對模塊進行操作</string>
@ -35,8 +38,8 @@
<string name="invalid_format">所選模塊的格式無效或檔案損毀</string>
<string name="local_install_title">本機安裝</string>
<string name="source_code">原始碼</string>
<string name="last_updated">最後更新:</string>
<string name="magisk_builtin_module">Magisk 內置模塊</string>
<string name="substratum_builtin_module">Substratum 內置模塊</string>
<string name="force_dark_terminal_title">終端強制深色模式</string>
<string name="file_picker_failure">您當前的檔案管理員無法使用</string>
<string name="remote_install_title">遠程安裝</string>

@ -6,11 +6,18 @@
<string name="updatable">Updatable</string>
<string name="installed">Installed</string>
<string name="online_repo">Online Repo</string>
<string name="showcase_mode">The application is in showcase mode</string>
<string name="showcase_mode">The application is in lockdown mode</string>
<string name="failed_download">Failed to download file.</string>
<string name="slow_modules">Modules took too long to boot, consider disabling some modules</string>
<string name="fail_internet">Fail to connect to the internet</string>
<string name="title_activity_settings">SettingsActivity</string>
<string name="app_update_available">Application update available</string>
<string name="app_update">Update</string>
<!-- Module section translation -->
<string name="module_last_update">Last update:</string>
<string name="module_repo">Repo:</string>
<string name="module_by">by</string>
<!-- Preference Titles -->
<!-- Note: Lockdown mode used to be called showcase mode -->
@ -36,8 +43,8 @@
<string name="invalid_format">The selected module is in an invalid format</string>
<string name="local_install_title">Local install</string>
<string name="source_code">Source code</string>
<string name="last_updated">Last update:</string>
<string name="magisk_builtin_module">Magisk builtin module</string>
<string name="substratum_builtin_module">Substratum builtin module</string>
<string name="force_dark_terminal_title">Force dark mode terminal</string>
<string name="file_picker_failure">Your current file picker failed to give access to the file.</string>
<string name="remote_install_title">Remote install</string>
@ -45,7 +52,8 @@
<string name="use_magisk_install_command_pref">Use magisk module install command</string>
<string name="use_magisk_install_command_desc">
During test it caused problems to the module install error diagnosis tool,
so I hid this option behind dev-mode, enable this at your own risk!
so I hid this option behind developer mode, enable this at your own risk!
</string>
<string name="dev_mode_enabled">Developer mode enabled</string>
<string name="force_english_pref">Force English language</string>
</resources>

@ -79,6 +79,4 @@
<style name="Theme.MagiskModuleManager"
parent="Theme.MagiskModuleManager.Light" />
<style name="Theme.MagiskModuleManager.Transparent"
parent="Theme.MagiskModuleManager.Transparent.Light" />
</resources>

@ -10,6 +10,12 @@
app:entries="@array/theme_values_names"
app:entryValues="@array/theme_values" />
<SwitchPreferenceCompat
app:defaultValue="false"
app:key="pref_force_english"
app:icon="@drawable/ic_baseline_language_24"
app:title="@string/force_english_pref" />
<SwitchPreferenceCompat
app:defaultValue="false"
app:key="pref_force_dark_terminal"
@ -54,6 +60,10 @@
</PreferenceCategory>
<PreferenceCategory
app:title="@string/pref_category_info">
<Preference
app:key="pref_update"
app:icon="@drawable/ic_baseline_system_update_24"
app:title="@string/app_update" />
<Preference
app:key="pref_source_code"
app:icon="@drawable/ic_github"

Loading…
Cancel
Save