Merge pull request #192 from Androidacy/master

Reworks Androidacy support
pull/27/head
Fox2Code 3 years ago committed by GitHub
commit 05931a3a36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,8 +2,7 @@
## Fox Module contest ## Fox Module contest
[NoStorageRestrict](https://github.com/Magisk-Modules-Alt-Repo/NoStorageRestrict) [NoStorageRestrict](https://github.com/Magisk-Modules-Alt-Repo/NoStorageRestrict) by [@DanGLES3](https://github.com/DanGLES3) won via vote in community telegram server.
by [@DanGLES3](https://github.com/DanGLES3) won via vote in community telegram server.
Module description: Removes the restriction when selecting folders (Downloads/Android) through the file manager on Android 11 and higher Module description: Removes the restriction when selecting folders (Downloads/Android) through the file manager on Android 11 and higher
@ -19,12 +18,11 @@ Main activity:
## What is this? ## What is this?
The official Magisk is dropping support to download online modules... The official Magisk has dropped support to download online modules, so I made Fox's Magisk Module Manager to help you download and install Magisk modules.
So I made my own app to do that! :3
**This app is not officially supported by Magisk or its developers** **This app is not officially supported by Magisk or its developers**
**The modules shown in this app are not affiliated with this app** **The modules shown in this app are not affiliated with this app or Magisk**
(Please contact repo owners instead) (Please contact repo owners instead)
## Requirements ## Requirements

@ -34,6 +34,7 @@ android {
"default" { "default" {
dimension "type" dimension "type"
buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "true" buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true"
buildConfigField( buildConfigField(
"java.util.List<String>", "java.util.List<String>",
"ENABLED_REPOS", "ENABLED_REPOS",
@ -50,6 +51,9 @@ android {
// with our keys, so the APK wouldn't install anyways). // with our keys, so the APK wouldn't install anyways).
buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "false" buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "false"
// Disable crash reporting for F-Droid flavor by default
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "false"
// Repo with ads or tracking feature are disabled by default for the // Repo with ads or tracking feature are disabled by default for the
// F-Droid flavor. // F-Droid flavor.
buildConfigField( buildConfigField(
@ -79,6 +83,8 @@ configurations {
} }
dependencies { dependencies {
// Error reporting
implementation 'io.sentry:sentry-android:6.4.0'
// UI // UI
implementation 'androidx.appcompat:appcompat:1.5.0' implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'androidx.emoji2:emoji2:1.2.0' implementation 'androidx.emoji2:emoji2:1.2.0'
@ -87,7 +93,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.webkit:webkit:1.4.0' implementation 'androidx.webkit:webkit:1.5.0'
implementation 'com.google.android.material:material:1.6.1' implementation 'com.google.android.material:material:1.6.1'
implementation "com.mikepenz:aboutlibraries:${latestAboutLibsRelease}" implementation "com.mikepenz:aboutlibraries:${latestAboutLibsRelease}"
implementation "dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0" implementation "dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0"

@ -5,7 +5,9 @@
tools:ignore="QueryAllPackagesPermission"> tools:ignore="QueryAllPackagesPermission">
<!-- Wifi is not the only way to get an internet connection --> <!-- Wifi is not the only way to get an internet connection -->
<uses-feature android:name="android.hardware.wifi" android:required="false" /> <uses-feature
android:name="android.hardware.wifi"
android:required="false" />
<!-- Retrieve online modules --> <!-- Retrieve online modules -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -16,7 +18,8 @@
<!-- Open config apps for applications --> <!-- Open config apps for applications -->
<uses-permission-sdk-23 android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission-sdk-23 android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- Supposed to fix bugs with old firmware, only requested on pre Marshmallow --> <!-- Supposed to fix bugs with old firmware, only requested on pre Marshmallow -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" <uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="22" /> android:maxSdkVersion="22" />
<!-- Post background notifications --> <!-- Post background notifications -->
<uses-permission-sdk-23 android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission-sdk-23 android:name="android.permission.POST_NOTIFICATIONS" />
@ -24,30 +27,32 @@
<application <application
android:name=".MainApplication" android:name=".MainApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/full_backup_content"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="@bool/lang_support_rtl" android:supportsRtl="@bool/lang_support_rtl"
android:testOnly="false" android:testOnly="false"
android:theme="@style/Theme.MagiskModuleManager" android:theme="@style/Theme.MagiskModuleManager"
android:fullBackupContent="@xml/full_backup_content"
android:dataExtractionRules="@xml/data_extraction_rules"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="false" android:usesCleartextTraffic="false"
tools:targetApi="s" tools:ignore="ManifestResource"
tools:replace="android:supportsRtl" tools:replace="android:supportsRtl"
tools:ignore="ManifestResource"> tools:targetApi="s">
<receiver android:name="com.fox2code.mmm.background.BackgroundBootListener" <receiver
android:name="com.fox2code.mmm.background.BackgroundBootListener"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<activity <activity
android:name=".settings.SettingsActivity" android:name=".settings.SettingsActivity"
android:parentActivityName=".MainActivity"
android:exported="true" android:exported="true"
android:label="@string/title_activity_settings"> android:label="@string/title_activity_settings"
android:parentActivityName=".MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" /> <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
</intent-filter> </intent-filter>
@ -55,8 +60,8 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTask" android:label="@string/app_name_short"
android:label="@string/app_name_short"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -64,10 +69,10 @@
</activity> </activity>
<activity <activity
android:name=".installer.InstallerActivity" android:name=".installer.InstallerActivity"
android:parentActivityName=".MainActivity"
android:exported="false" android:exported="false"
android:screenOrientation="portrait"
android:launchMode="singleTop" android:launchMode="singleTop"
android:parentActivityName=".MainActivity"
android:screenOrientation="portrait"
tools:ignore="LockedOrientationActivity"> tools:ignore="LockedOrientationActivity">
<intent-filter> <intent-filter>
<action android:name="${applicationId}.intent.action.INSTALL_MODULE_INTERNAL" /> <action android:name="${applicationId}.intent.action.INSTALL_MODULE_INTERNAL" />
@ -75,30 +80,45 @@
</activity> </activity>
<activity <activity
android:name=".markdown.MarkdownActivity" android:name=".markdown.MarkdownActivity"
android:parentActivityName=".MainActivity"
android:exported="false" android:exported="false"
android:theme="@style/Theme.MagiskModuleManager"> android:parentActivityName=".MainActivity"
</activity> android:theme="@style/Theme.MagiskModuleManager"></activity>
<activity <activity
android:name=".androidacy.AndroidacyActivity" android:name=".androidacy.AndroidacyActivity"
android:parentActivityName=".MainActivity"
android:exported="false" android:exported="false"
android:parentActivityName=".MainActivity"
android:theme="@style/Theme.MagiskModuleManager"> android:theme="@style/Theme.MagiskModuleManager">
<intent-filter> <intent-filter>
<action android:name="${applicationId}.intent.action.OPEN_ANDROIDACY_INTERNAL" /> <action android:name="${applicationId}.intent.action.OPEN_ANDROIDACY_INTERNAL" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.mikepenz.aboutlibraries.ui.LibsActivity" <activity
tools:node="remove"/> android:name="com.mikepenz.aboutlibraries.ui.LibsActivity"
tools:node="remove" />
<provider <provider
android:name="androidx.startup.InitializationProvider" android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup" android:authorities="${applicationId}.androidx-startup"
android:exported="false" android:exported="false"
tools:node="merge"> tools:node="merge">
<meta-data android:name="androidx.emoji2.text.EmojiCompatInitializer" <meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
tools:node="remove" /> tools:node="remove" />
<meta-data android:name="androidx.work.WorkManagerInitializer" <meta-data
android:name="androidx.work.WorkManagerInitializer"
tools:node="remove" /> tools:node="remove" />
</provider> </provider>
<meta-data
android:name="io.sentry.dsn"
android:value="https://ba5cb5ef513b423cbd54a2a8457113b1@sentry.androidacy.com/7" />
<!-- Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
We recommend adjusting this value in production. -->
<meta-data
android:name="io.sentry.traces.sample-rate"
android:value="1.0" />
<!-- Enable user interaction tracing to capture transactions for various UI events (such as clicks or scrolls). -->
<meta-data
android:name="io.sentry.traces.user-interaction.enable"
android:value="true" />
</application> </application>
</manifest> </manifest>

@ -37,6 +37,7 @@ import com.google.android.material.progressindicator.LinearProgressIndicator;
import eightbitlab.com.blurview.BlurView; import eightbitlab.com.blurview.BlurView;
import eightbitlab.com.blurview.RenderScriptBlur; import eightbitlab.com.blurview.RenderScriptBlur;
import io.sentry.android.core.SentryAndroid;
public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener, public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener,
SearchView.OnQueryTextListener, SearchView.OnCloseListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener,

@ -27,6 +27,8 @@ import com.fox2code.mmm.utils.Http;
import com.fox2code.rosettax.LanguageSwitcher; import com.fox2code.rosettax.LanguageSwitcher;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
@ -42,6 +44,9 @@ import io.noties.markwon.syntax.Prism4jThemeDefault;
import io.noties.markwon.syntax.SyntaxHighlightPlugin; import io.noties.markwon.syntax.SyntaxHighlightPlugin;
import io.noties.prism4j.Prism4j; import io.noties.prism4j.Prism4j;
import io.noties.prism4j.annotations.PrismBundle; import io.noties.prism4j.annotations.PrismBundle;
import io.sentry.JsonObjectWriter;
import io.sentry.NoOpLogger;
import io.sentry.android.core.SentryAndroid;
@PrismBundle( @PrismBundle(
includeAll = true, includeAll = true,
@ -49,6 +54,7 @@ import io.noties.prism4j.annotations.PrismBundle;
) )
public class MainApplication extends FoxApplication public class MainApplication extends FoxApplication
implements androidx.work.Configuration.Provider { implements androidx.work.Configuration.Provider {
private static final String TAG = "MainApplication";
private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001 private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001
private static Locale timeFormatLocale = private static Locale timeFormatLocale =
Resources.getSystem().getConfiguration().locale; Resources.getSystem().getConfiguration().locale;
@ -168,6 +174,11 @@ public class MainApplication extends FoxApplication
getSharedPreferences().edit().putBoolean("has_root_access", bool).apply(); getSharedPreferences().edit().putBoolean("has_root_access", bool).apply();
} }
public static boolean isCrashReportingEnabled() {
return getSharedPreferences().getBoolean(
"crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING);
}
public static SharedPreferences getBootSharedPreferences() { public static SharedPreferences getBootSharedPreferences() {
return bootSharedPreferences; return bootSharedPreferences;
} }
@ -341,6 +352,57 @@ public class MainApplication extends FoxApplication
Log.d("MainApplication", "Emoji compat loaded!"); Log.d("MainApplication", "Emoji compat loaded!");
}, "Emoji compat init.").start(); }, "Emoji compat init.").start();
} }
SentryAndroid.init(this, options -> {
// Note: Sentry library only take a screenshot of Fox Magisk Module Manager.
// The screen shot doesn't and cannot contain other applications (if in multi windows)
// status bar and notifications (even if notification shade is pulled down)
// In the possibility you find this library sending anything listed above,
// it's a serious bug and a security issue you should report to Google
// Google bug bounties on Android are huge, so you can also get rich by doing that.
options.setAttachScreenshot(true);
// Add a callback that will be used before the event is sent to Sentry.
// With this callback, you can modify the event or, when returning null, also discard the event.
options.setBeforeSend((event, hint) -> {
if (BuildConfig.DEBUG) { // Debug sentry events for debug.
StringBuilder stringBuilder = new StringBuilder("Sentry report debug: ");
try {
event.serialize(new JsonObjectWriter(new Writer() {
@Override
public void write(char[] cbuf) {
stringBuilder.append(cbuf);
}
@Override
public void write(char[] chars, int i, int i1) {
stringBuilder.append(chars, i, i1);
}
@Override
public void write(String str, int off, int len) {
stringBuilder.append(str, off, len);
}
@Override
public void flush() {}
@Override
public void close() {}
}, 4), NoOpLogger.getInstance());
} catch (IOException ignored) {}
Log.i(TAG, stringBuilder.toString());
}
// Check saved preferences to see if the user has opted out of crash reporting.
// If the user has opted out, return null.
if (isCrashReportingEnabled()) {
Log.i(TAG, "Relayed sentry report according to user preference");
return event;
} else {
Log.i(TAG, "Blocked sentry report according to user preference");
return null;
}
});
});
} }
@Override @Override

@ -21,24 +21,23 @@ import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import okhttp3.Cookie;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
@SuppressWarnings("KotlinInternalInJava") @SuppressWarnings("KotlinInternalInJava")
public final class AndroidacyRepoData extends RepoData { public final class AndroidacyRepoData extends RepoData {
private static final String TAG = "AndroidacyRepoData"; private static final String TAG = "AndroidacyRepoData";
private static final HttpUrl OK_HTTP_URL;
static { static {
HttpUrl.Builder OK_HTTP_URL_BUILDER = HttpUrl.Builder OK_HTTP_URL_BUILDER =
new HttpUrl.Builder().scheme("https"); new HttpUrl.Builder().scheme("https");
// Using HttpUrl.Builder.host(String) crash the app // Using HttpUrl.Builder.host(String) crash the app
OK_HTTP_URL_BUILDER.setHost$okhttp(".androidacy.com"); OK_HTTP_URL_BUILDER.setHost$okhttp(".androidacy.com");
OK_HTTP_URL = OK_HTTP_URL_BUILDER.build(); OK_HTTP_URL_BUILDER.build();
} }
// Avoid spamming requests to Androidacy // Avoid spamming requests to Androidacy
private long androidacyBlockade = 0; private long androidacyBlockade = 0;
private String token = null; private String token = null;
@ -60,30 +59,36 @@ public final class AndroidacyRepoData extends RepoData {
this.defaultSupport = "https://t.me/androidacy_discussions"; this.defaultSupport = "https://t.me/androidacy_discussions";
this.defaultDonate = "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm-medium=app&utm_campaign=fox-inapp"; this.defaultDonate = "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm-medium=app&utm_campaign=fox-inapp";
this.defaultSubmitModule = "https://www.androidacy.com/module-repository-applications/"; this.defaultSubmitModule = "https://www.androidacy.com/module-repository-applications/";
this.host = testMode ? "staging-api.androidacy.com" : "api.androidacy.com"; this.host = testMode ? "staging-api.androidacy.com" : "production-api.androidacy.com";
this.testMode = testMode; this.testMode = testMode;
} }
private static String getCookies() {
if (Http.hasWebView()) {
return CookieManager.getInstance().getCookie("https://.androidacy.com/");
} else {
Iterator<Cookie> cookies = Http.getCookieJar()
.loadForRequest(OK_HTTP_URL).iterator();
if (!cookies.hasNext()) return "";
StringBuilder stringBuilder = new StringBuilder();
while (true) {
stringBuilder.append(cookies.next().toString());
if (!cookies.hasNext()) return stringBuilder.toString();
stringBuilder.append(",");
}
}
}
public static AndroidacyRepoData getInstance() { public static AndroidacyRepoData getInstance() {
return RepoManager.getINSTANCE().getAndroidacyRepoData(); return RepoManager.getINSTANCE().getAndroidacyRepoData();
} }
public <string> boolean isValidToken(string token) {
try {
Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token, false);
} catch (Exception e) {
if ("Received error code: 419".equals(e.getMessage()) ||
"Received error code: 429".equals(e.getMessage())) {
Log.e(TAG, "We are being rate limited!", e);
long time = System.currentTimeMillis();
this.androidacyBlockade = time + 3_600_000L;
return false;
}
Log.w(TAG, "Invalid token, resetting...");
// Remove saved preference
SharedPreferences.Editor editor = this.cachedPreferences.edit();
editor.remove("androidacy_api_token");
editor.apply();
return false;
}
// If status code is 200, we are good
return true;
}
@Override @Override
protected boolean prepare() { protected boolean prepare() {
// Implementation details discussed on telegram // Implementation details discussed on telegram
@ -91,57 +96,33 @@ public final class AndroidacyRepoData extends RepoData {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
if (this.androidacyBlockade > time) return false; if (this.androidacyBlockade > time) return false;
this.androidacyBlockade = time + 30_000L; this.androidacyBlockade = time + 30_000L;
String cookies = AndroidacyRepoData.getCookies(); // Get token from androidacy_api_token shared preference
int start = cookies == null ? -1 : cookies.indexOf("USER=") + 5; String token = this.cachedPreferences.getString("androidacy_api_token", null);
String token = null;
if (start != -1) {
int end = cookies.indexOf(";", start);
if (end != -1)
token = cookies.substring(start, end);
}
if (token != null) { if (token != null) {
try { this.token = token;
Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token, true); if (!isValidToken(token)) {
} catch (Exception e) { token = null;
if ("Received error code: 419".equals(e.getMessage()) ||
"Received error code: 429".equals(e.getMessage())) {
Log.e(TAG, "We are being rate limited!", e);
this.androidacyBlockade = time + 3_600_000L;
return false;
}
Log.w(TAG, "Invalid token, resetting...");
if (Http.hasWebView()) {
CookieManager.getInstance().setCookie("https://.androidacy.com/",
"USER=; expires=Thu, 01 Jan 1970 00:00:00 GMT;" +
" path=/; secure; domain=.androidacy.com");
} else {
Http.getCookieJar().saveFromResponse(
OK_HTTP_URL, Collections.emptyList());
}
this.token = token = null;
} }
} }
if (token == null) { if (token == null) {
try { try {
Log.i(TAG, "Refreshing token..."); Log.i(TAG, "Refreshing token...");
// POST request to https://production-api.androidacy.com/auth/register
token = new String(Http.doHttpPost( token = new String(Http.doHttpPost(
"https://" + this.host + "/auth/register", "https://" + this.host + "/auth/register",
"",true), StandardCharsets.UTF_8); "foxmmm=true", true), StandardCharsets.UTF_8);
if (Http.hasWebView()) { // Parse token
CookieManager.getInstance().setCookie("https://.androidacy.com/", JSONObject jsonObject = new JSONObject(token);
"USER=" + token + "; expires=Fri, 31 Dec 9999 23:59:59 GMT;" + token = jsonObject.getString("token");
" path=/; secure; domain=.androidacy.com"); // Save token to shared preference
} else { SharedPreferences.Editor editor = this.cachedPreferences.edit();
Http.getCookieJar().saveFromResponse(OK_HTTP_URL, editor.putString("androidacy_api_token", token);
Collections.singletonList(Cookie.parse(OK_HTTP_URL, editor.apply();
"USER=" + token + "; expires=Fri, 31 Dec 9999 23:59:59 GMT;" +
" path=/; secure; domain=.androidacy.com")));
}
} catch (Exception e) { } catch (Exception e) {
if ("Received error code: 419".equals(e.getMessage()) || if ("Received error code: 419".equals(e.getMessage()) ||
"Received error code: 429".equals(e.getMessage()) || "Received error code: 429".equals(e.getMessage()) ||
"Received error code: 503".equals(e.getMessage()) "Received error code: 503".equals(e.getMessage())
) { ) {
Log.e(TAG, "We are being rate limited!", e); Log.e(TAG, "We are being rate limited!", e);
this.androidacyBlockade = time + 3_600_000L; this.androidacyBlockade = time + 3_600_000L;
} }
@ -194,7 +175,7 @@ public final class AndroidacyRepoData extends RepoData {
jsonObject.optString("zipUrl", "")); jsonObject.optString("zipUrl", ""));
repoModule.notesUrl = filterURL( repoModule.notesUrl = filterURL(
jsonObject.optString("notesUrl", "")); jsonObject.optString("notesUrl", ""));
if (repoModule.zipUrl == null) { if (repoModule.zipUrl == null) {
repoModule.zipUrl = // Fallback url in case the API doesn't have zipUrl repoModule.zipUrl = // Fallback url in case the API doesn't have zipUrl
"https://" + this.host + "/magisk/info/" + moduleId; "https://" + this.host + "/magisk/info/" + moduleId;
} }
@ -264,7 +245,8 @@ public final class AndroidacyRepoData extends RepoData {
} }
@Override @Override
public void storeMetadata(RepoModule repoModule, byte[] data) {} public void storeMetadata(RepoModule repoModule, byte[] data) {
}
@Override @Override
public boolean tryLoadMetadata(RepoModule repoModule) { public boolean tryLoadMetadata(RepoModule repoModule) {

@ -40,7 +40,7 @@ public final class RepoManager extends SyncManager {
"https://cdn.jsdelivr.net/gh/Magisk-Modules-Alt-Repo/json@main/modules.json"; "https://cdn.jsdelivr.net/gh/Magisk-Modules-Alt-Repo/json@main/modules.json";
public static final String ANDROIDACY_MAGISK_REPO_ENDPOINT = public static final String ANDROIDACY_MAGISK_REPO_ENDPOINT =
"https://api.androidacy.com/magisk/repo"; "https://production-api.androidacy.com/magisk/repo";
public static final String ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT = public static final String ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT =
"https://staging-api.androidacy.com/magisk/repo"; "https://staging-api.androidacy.com/magisk/repo";
public static final String ANDROIDACY_MAGISK_REPO_HOMEPAGE = public static final String ANDROIDACY_MAGISK_REPO_HOMEPAGE =

@ -8,7 +8,10 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import android.view.inputmethod.EditorInfo;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Button; import android.widget.Button;
import android.widget.Toast; import android.widget.Toast;
@ -17,10 +20,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreferenceCompat;
import androidx.preference.TwoStatePreference; import androidx.preference.TwoStatePreference;
import com.fox2code.foxcompat.FoxActivity; import com.fox2code.foxcompat.FoxActivity;
@ -32,6 +38,7 @@ import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainActivity; import com.fox2code.mmm.MainActivity;
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R; import com.fox2code.mmm.R;
import com.fox2code.mmm.androidacy.AndroidacyRepoData;
import com.fox2code.mmm.background.BackgroundUpdateChecker; import com.fox2code.mmm.background.BackgroundUpdateChecker;
import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.module.ActionButtonType; import com.fox2code.mmm.module.ActionButtonType;
@ -45,6 +52,7 @@ import com.fox2code.rosettax.LanguageActivity;
import com.fox2code.rosettax.LanguageSwitcher; import com.fox2code.rosettax.LanguageSwitcher;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.internal.TextWatcherAdapter; import com.google.android.material.internal.TextWatcherAdapter;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.MaterialAutoCompleteTextView; import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.mikepenz.aboutlibraries.LibsBuilder; import com.mikepenz.aboutlibraries.LibsBuilder;
import com.topjohnwu.superuser.internal.UiThreadHandler; import com.topjohnwu.superuser.internal.UiThreadHandler;
@ -70,10 +78,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
setTitle(R.string.app_name); setTitle(R.string.app_name);
setActionBarBackground(null); setActionBarBackground(null);
if (savedInstanceState == null) { if (savedInstanceState == null) {
getSupportFragmentManager() getSupportFragmentManager().beginTransaction().replace(R.id.settings, new SettingsFragment()).commit();
.beginTransaction()
.replace(R.id.settings, new SettingsFragment())
.commit();
} }
} }
@ -83,9 +88,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
Intent mStartActivity = new Intent(this, MainActivity.class); Intent mStartActivity = new Intent(this, MainActivity.class);
mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
int mPendingIntentId = 123456; int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(this, mPendingIntentId, PendingIntent mPendingIntent = PendingIntent.getActivity(this, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
AlarmManager mgr = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0); // Exit app process System.exit(0); // Exit app process
} }
@ -96,8 +100,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
super.onPause(); super.onPause();
} }
public static class SettingsFragment extends PreferenceFragmentCompat public static class SettingsFragment extends PreferenceFragmentCompat implements FoxActivity.OnBackPressedCallback {
implements FoxActivity.OnBackPressedCallback {
@Override @Override
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@ -119,11 +122,18 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
devModeStep = 0; devModeStep = 0;
UiThreadHandler.handler.postDelayed(() -> { UiThreadHandler.handler.postDelayed(() -> {
MainApplication.getINSTANCE().updateTheme(); MainApplication.getINSTANCE().updateTheme();
FoxActivity.getFoxActivity(this).setThemeRecreate( FoxActivity.getFoxActivity(this).setThemeRecreate(MainApplication.getINSTANCE().getManagerThemeResId());
MainApplication.getINSTANCE().getManagerThemeResId());
}, 1); }, 1);
return true; return true;
}); });
// Crash reporting
TwoStatePreference crashReportingPreference = findPreference("pref_crash_reporting");
crashReportingPreference.setChecked(MainApplication.isCrashReportingEnabled());
crashReportingPreference.setOnPreferenceChangeListener((preference, newValue) -> {
devModeStep = 0;
getCrashReportingEditor(requireActivity()).putBoolean("crash_reporting", (boolean) newValue).apply();
return true;
});
Preference enableBlur = findPreference("pref_enable_blur"); Preference enableBlur = findPreference("pref_enable_blur");
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
enableBlur.setSummary(R.string.require_android_6); enableBlur.setSummary(R.string.require_android_6);
@ -138,8 +148,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
disableMonet.setOnPreferenceClickListener(preference -> { disableMonet.setOnPreferenceClickListener(preference -> {
UiThreadHandler.handler.postDelayed(() -> { UiThreadHandler.handler.postDelayed(() -> {
MainApplication.getINSTANCE().updateTheme(); MainApplication.getINSTANCE().updateTheme();
((FoxActivity) this.requireActivity()).setThemeRecreate( ((FoxActivity) this.requireActivity()).setThemeRecreate(MainApplication.getINSTANCE().getManagerThemeResId());
MainApplication.getINSTANCE().getManagerThemeResId());
}, 1); }, 1);
return true; return true;
}); });
@ -182,8 +191,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
int level = this.currentLanguageLevel(); int level = this.currentLanguageLevel();
if (level != LANGUAGE_SUPPORT_LEVEL) { if (level != LANGUAGE_SUPPORT_LEVEL) {
Log.e(TAG, "Detected language level " + level + Log.e(TAG, "Detected language level " + level + ", latest is " + LANGUAGE_SUPPORT_LEVEL);
", latest is " + LANGUAGE_SUPPORT_LEVEL);
languageSelector.setSummary(R.string.language_support_outdated); languageSelector.setSummary(R.string.language_support_outdated);
} else { } else {
if (!"Translated by Fox2Code".equals( // I don't "translate" english if (!"Translated by Fox2Code".equals( // I don't "translate" english
@ -196,45 +204,45 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
if (!MainApplication.isDeveloper()) { if (!MainApplication.isDeveloper()) {
findPreference("pref_disable_low_quality_module_filter").setVisible(false); findPreference("pref_disable_low_quality_module_filter").setVisible(false);
// Hide the pref_crash option if not in debug mode - stop users from purposely crashing the app
Objects.requireNonNull((Preference) findPreference("pref_crash")).setVisible(false);
} else {
findPreference("pref_crash").setOnPreferenceClickListener(preference -> {
// Hard crash the app
throw new RuntimeException("This is a test crash");
});
} }
if (InstallerInitializer.peekMagiskVersion() < Constants.MAGISK_VER_CODE_INSTALL_COMMAND if (InstallerInitializer.peekMagiskVersion() < Constants.MAGISK_VER_CODE_INSTALL_COMMAND || !MainApplication.isDeveloper()) {
|| !MainApplication.isDeveloper()) {
findPreference("pref_use_magisk_install_command").setVisible(false); findPreference("pref_use_magisk_install_command").setVisible(false);
} }
Preference debugNotification = findPreference("pref_background_update_check_debug"); Preference debugNotification = findPreference("pref_background_update_check_debug");
debugNotification.setEnabled(MainApplication.isBackgroundUpdateCheckEnabled()); debugNotification.setEnabled(MainApplication.isBackgroundUpdateCheckEnabled());
debugNotification.setVisible(MainApplication.isDeveloper()); debugNotification.setVisible(MainApplication.isDeveloper());
debugNotification.setOnPreferenceClickListener(preference -> { debugNotification.setOnPreferenceClickListener(preference -> {
BackgroundUpdateChecker.postNotification( BackgroundUpdateChecker.postNotification(this.requireContext(), new Random().nextInt(4) + 2);
this.requireContext(), new Random().nextInt(4) + 2);
return true; return true;
}); });
findPreference("pref_background_update_check").setOnPreferenceChangeListener((preference, newValue) -> { findPreference("pref_background_update_check").setOnPreferenceChangeListener((preference, newValue) -> {
boolean enabled = Boolean.parseBoolean(String.valueOf(newValue)); boolean enabled = Boolean.parseBoolean(String.valueOf(newValue));
debugNotification.setEnabled(enabled); debugNotification.setEnabled(enabled);
if (!enabled) { if (!enabled) {
BackgroundUpdateChecker.onMainActivityResume( BackgroundUpdateChecker.onMainActivityResume(this.requireContext());
this.requireContext());
} }
return true; return true;
}); });
final LibsBuilder libsBuilder = new LibsBuilder().withShowLoadingProgress(false) final LibsBuilder libsBuilder = new LibsBuilder().withShowLoadingProgress(false).withLicenseShown(true).withAboutMinimalDesign(false);
.withLicenseShown(true).withAboutMinimalDesign(false);
Preference update = findPreference("pref_update"); Preference update = findPreference("pref_update");
update.setVisible(BuildConfig.ENABLE_AUTO_UPDATER && (BuildConfig.DEBUG || update.setVisible(BuildConfig.ENABLE_AUTO_UPDATER && (BuildConfig.DEBUG || AppUpdateManager.getAppUpdateManager().peekHasUpdate()));
AppUpdateManager.getAppUpdateManager().peekHasUpdate()));
update.setOnPreferenceClickListener(p -> { update.setOnPreferenceClickListener(p -> {
devModeStep = 0; devModeStep = 0;
IntentHelper.openUrl(p.getContext(), IntentHelper.openUrl(p.getContext(), "https://github.com/Fox2Code/FoxMagiskModuleManager/releases");
"https://github.com/Fox2Code/FoxMagiskModuleManager/releases");
return true; return true;
}); });
if (BuildConfig.DEBUG || BuildConfig.ENABLE_AUTO_UPDATER) { if (BuildConfig.DEBUG || BuildConfig.ENABLE_AUTO_UPDATER) {
findPreference("pref_report_bug").setOnPreferenceClickListener(p -> { findPreference("pref_report_bug").setOnPreferenceClickListener(p -> {
devModeStep = 0; devModeStep = 0;
IntentHelper.openUrl(p.getContext(), IntentHelper.openUrl(p.getContext(), "https://github.com/Fox2Code/FoxMagiskModuleManager/issues");
"https://github.com/Fox2Code/FoxMagiskModuleManager/issues");
return true; return true;
}); });
} else { } else {
@ -244,20 +252,17 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
if (devModeStep == 2) { if (devModeStep == 2) {
devModeStep = 0; devModeStep = 0;
if (MainApplication.isDeveloper() && !BuildConfig.DEBUG) { if (MainApplication.isDeveloper() && !BuildConfig.DEBUG) {
MainApplication.getSharedPreferences().edit() MainApplication.getSharedPreferences().edit().putBoolean("developer", false).apply();
.putBoolean("developer", false).apply();
Toast.makeText(getContext(), // Tell the user something changed Toast.makeText(getContext(), // Tell the user something changed
R.string.dev_mode_disabled, Toast.LENGTH_SHORT).show(); R.string.dev_mode_disabled, Toast.LENGTH_SHORT).show();
} else { } else {
MainApplication.getSharedPreferences().edit() MainApplication.getSharedPreferences().edit().putBoolean("developer", true).apply();
.putBoolean("developer", true).apply();
Toast.makeText(getContext(), // Tell the user something changed Toast.makeText(getContext(), // Tell the user something changed
R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show(); R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show();
} }
return true; return true;
} }
IntentHelper.openUrl(p.getContext(), IntentHelper.openUrl(p.getContext(), "https://github.com/Fox2Code/FoxMagiskModuleManager");
"https://github.com/Fox2Code/FoxMagiskModuleManager");
return true; return true;
}); });
findPreference("pref_support").setOnPreferenceClickListener(p -> { findPreference("pref_support").setOnPreferenceClickListener(p -> {
@ -271,43 +276,31 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
openFragment(libsBuilder.supportFragment(), R.string.licenses); openFragment(libsBuilder.supportFragment(), R.string.licenses);
return true; return true;
}); });
findPreference("pref_pkg_info").setSummary( findPreference("pref_pkg_info").setSummary(BuildConfig.APPLICATION_ID + " v" + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")");
BuildConfig.APPLICATION_ID + " v" + }
BuildConfig.VERSION_NAME + " (" +
BuildConfig.VERSION_CODE + ")"); private SharedPreferences.Editor getCrashReportingEditor(FragmentActivity requireActivity) {
return requireActivity.getSharedPreferences("crash_reporting", Context.MODE_PRIVATE).edit();
} }
private void openFragment(Fragment fragment, @StringRes int title) { private void openFragment(Fragment fragment, @StringRes int title) {
FoxActivity compatActivity = getFoxActivity(this); FoxActivity compatActivity = getFoxActivity(this);
compatActivity.setOnBackPressedCallback(this); compatActivity.setOnBackPressedCallback(this);
compatActivity.setTitle(title); compatActivity.setTitle(title);
compatActivity.getSupportFragmentManager() compatActivity.getSupportFragmentManager().beginTransaction().replace(R.id.settings, fragment).setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).commit();
.beginTransaction()
.replace(R.id.settings, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
} }
@Override @Override
public boolean onBackPressed(FoxActivity compatActivity) { public boolean onBackPressed(FoxActivity compatActivity) {
compatActivity.setTitle(R.string.app_name); compatActivity.setTitle(R.string.app_name);
compatActivity.getSupportFragmentManager() compatActivity.getSupportFragmentManager().beginTransaction().replace(R.id.settings, this).setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).commit();
.beginTransaction().replace(R.id.settings, this)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
return true; return true;
} }
private int currentLanguageLevel() { private int currentLanguageLevel() {
int declaredLanguageLevel = int declaredLanguageLevel = this.getResources().getInteger(R.integer.language_support_level);
this.getResources().getInteger(R.integer.language_support_level); if (declaredLanguageLevel != LANGUAGE_SUPPORT_LEVEL) return declaredLanguageLevel;
if (declaredLanguageLevel != LANGUAGE_SUPPORT_LEVEL) if (!this.getResources().getConfiguration().locale.getLanguage().equals("en") && this.getResources().getString(R.string.notification_update_pref).equals("Background modules update check") && this.getResources().getString(R.string.notification_update_desc).equals("May increase battery usage")) {
return declaredLanguageLevel;
if (!this.getResources().getConfiguration().locale.getLanguage().equals("en") &&
this.getResources().getString(R.string.notification_update_pref)
.equals("Background modules update check") &&
this.getResources().getString(R.string.notification_update_desc)
.equals("May increase battery usage")) {
return 0; return 0;
} }
return LANGUAGE_SUPPORT_LEVEL; return LANGUAGE_SUPPORT_LEVEL;
@ -317,6 +310,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
public static class RepoFragment extends PreferenceFragmentCompat { public static class RepoFragment extends PreferenceFragmentCompat {
private static final int CUSTOM_REPO_ENTRIES = 5; private static final int CUSTOM_REPO_ENTRIES = 5;
@SuppressLint("RestrictedApi")
@Override @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
getPreferenceManager().setSharedPreferencesName("mmm"); getPreferenceManager().setSharedPreferencesName("mmm");
@ -326,28 +320,101 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
setRepoData(RepoManager.DG_MAGISK_REPO_GITHUB); setRepoData(RepoManager.DG_MAGISK_REPO_GITHUB);
updateCustomRepoList(true); updateCustomRepoList(true);
if (!MainApplication.isDeveloper()) { if (!MainApplication.isDeveloper()) {
Objects.requireNonNull((Preference) findPreference( Objects.requireNonNull((Preference) findPreference("pref_androidacy_test_mode")).setVisible(false);
"pref_androidacy_test_mode")).setVisible(false); } else {
// Show a warning if user tries to enable test mode
Objects.requireNonNull((SwitchPreferenceCompat) findPreference("pref_androidacy_test_mode")).setOnPreferenceChangeListener((preference, newValue) -> {
if (Boolean.parseBoolean(String.valueOf(newValue))) {
new AlertDialog.Builder(this.requireContext())
.setTitle(R.string.warning)
.setMessage(R.string.androidacy_test_mode_warning)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
// Do nothing
})
.show();
}
return true;
});
} }
String originalApiKey = MainApplication.getSharedPreferences().getString("pref_androidacy_api_token", "");
// Create the pref_androidacy_repo_api_key text input with validation
EditTextPreference prefAndroidacyRepoApiKey = findPreference("pref_androidacy_repo_api_key");
assert prefAndroidacyRepoApiKey != null;
prefAndroidacyRepoApiKey.setOnBindEditTextListener(editText -> {
editText.setSingleLine();
// Make the single line wrap
editText.setHorizontallyScrolling(false);
// Set the height to the height of 2 lines
editText.setHeight(editText.getLineHeight() * 3);
// Make ok button say "Save"
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
});
prefAndroidacyRepoApiKey.setPositiveButtonText(R.string.save_api_key);
prefAndroidacyRepoApiKey.setOnPreferenceChangeListener((preference, newValue) -> {
// Curious if this actually works - so crash the app on purpose
// throw new RuntimeException("This is a test crash");
// get original api key
String apiKey = String.valueOf(newValue);
// Show snack bar with indeterminate progress
Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> {
// Restore the original api key
prefAndroidacyRepoApiKey.setText(originalApiKey);
}).show();
// Check the API key on a background thread
new Thread(() -> {
// If key is empty, just remove it and change the text of the snack bar
if (apiKey.isEmpty()) {
MainApplication.getSharedPreferences().edit().remove("pref_androidacy_repo_api_key").apply();
new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show());
} else {
// If key < 64 chars, it's not valid
if (apiKey.length() < 64) {
new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show();
// Save the original key
MainApplication.getSharedPreferences().edit().putString("pref_androidacy_api_token", originalApiKey).apply();
// Re-show the dialog with an error
prefAndroidacyRepoApiKey.performClick();
// Show error
prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid));
});
} else {
boolean valid = AndroidacyRepoData.getInstance().isValidToken(apiKey);
// If the key is valid, save it
if (valid) {
MainApplication.getSharedPreferences().edit().putString("pref_androidacy_repo_api_key", apiKey).apply();
new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_valid, Snackbar.LENGTH_SHORT).show());
} else {
new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show();
// Save the original key
MainApplication.getSharedPreferences().edit().putString("pref_androidacy_api_token", originalApiKey).apply();
// Re-show the dialog with an error
prefAndroidacyRepoApiKey.performClick();
// Show error
prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid));
});
}
}
}
}).start();
return true;
});
} }
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
public void updateCustomRepoList(boolean initial) { public void updateCustomRepoList(boolean initial) {
final SharedPreferences sharedPreferences = Objects.requireNonNull( final SharedPreferences sharedPreferences = Objects.requireNonNull(this.getPreferenceManager().getSharedPreferences());
this.getPreferenceManager().getSharedPreferences()); final CustomRepoManager customRepoManager = RepoManager.getINSTANCE().getCustomRepoManager();
final CustomRepoManager customRepoManager =
RepoManager.getINSTANCE().getCustomRepoManager();
for (int i = 0; i < CUSTOM_REPO_ENTRIES; i++) { for (int i = 0; i < CUSTOM_REPO_ENTRIES; i++) {
CustomRepoData repoData = customRepoManager.getRepo(i); CustomRepoData repoData = customRepoManager.getRepo(i);
setRepoData(repoData, "pref_custom_repo_" + i); setRepoData(repoData, "pref_custom_repo_" + i);
if (initial) { if (initial) {
Preference preference = Preference preference = findPreference("pref_custom_repo_" + i + "_delete");
findPreference("pref_custom_repo_" + i + "_delete");
if (preference == null) continue; if (preference == null) continue;
final int index = i; final int index = i;
preference.setOnPreferenceClickListener(preference1 -> { preference.setOnPreferenceClickListener(preference1 -> {
sharedPreferences.edit().putBoolean( sharedPreferences.edit().putBoolean("pref_custom_repo_" + index + "_enabled", false).apply();
"pref_custom_repo_" + index + "_enabled", false).apply();
customRepoManager.removeRepo(index); customRepoManager.removeRepo(index);
updateCustomRepoList(false); updateCustomRepoList(false);
return true; return true;
@ -356,16 +423,14 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
} }
Preference preference = findPreference("pref_custom_add_repo"); Preference preference = findPreference("pref_custom_add_repo");
if (preference == null) return; if (preference == null) return;
preference.setVisible(customRepoManager.canAddRepo() && preference.setVisible(customRepoManager.canAddRepo() && customRepoManager.getRepoCount() < CUSTOM_REPO_ENTRIES);
customRepoManager.getRepoCount() < CUSTOM_REPO_ENTRIES);
if (initial) { // Custom repo add button part. if (initial) { // Custom repo add button part.
preference = findPreference("pref_custom_add_repo_button"); preference = findPreference("pref_custom_add_repo_button");
if (preference == null) return; if (preference == null) return;
preference.setOnPreferenceClickListener(preference1 -> { preference.setOnPreferenceClickListener(preference1 -> {
final Context context = this.requireContext(); final Context context = this.requireContext();
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context); MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
final MaterialAutoCompleteTextView input = final MaterialAutoCompleteTextView input = new MaterialAutoCompleteTextView(context);
new MaterialAutoCompleteTextView(context);
input.setHint(R.string.custom_url); input.setHint(R.string.custom_url);
builder.setIcon(R.drawable.ic_baseline_add_box_24); builder.setIcon(R.drawable.ic_baseline_add_box_24);
builder.setTitle(R.string.add_repo); builder.setTitle(R.string.add_repo);
@ -373,28 +438,24 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
builder.setPositiveButton("OK", (dialog, which) -> { builder.setPositiveButton("OK", (dialog, which) -> {
String text = String.valueOf(input.getText()); String text = String.valueOf(input.getText());
if (customRepoManager.canAddRepo(text)) { if (customRepoManager.canAddRepo(text)) {
final CustomRepoData customRepoData = final CustomRepoData customRepoData = customRepoManager.addRepo(text);
customRepoManager.addRepo(text);
customRepoData.setEnabled(true); customRepoData.setEnabled(true);
new Thread("Add Custom Repo Thread") { new Thread("Add Custom Repo Thread") {
@Override @Override
public void run() { public void run() {
try { try {
customRepoData.quickPrePopulate(); customRepoData.quickPrePopulate();
} catch (IOException|JSONException e) { } catch (IOException | JSONException e) {
Log.e(TAG, "Failed to preload repo values", e); Log.e(TAG, "Failed to preload repo values", e);
} }
UiThreadHandler.handler.post(() -> { UiThreadHandler.handler.post(() -> updateCustomRepoList(false));
updateCustomRepoList(false);
});
} }
}.start(); }.start();
} }
}); });
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
AlertDialog alertDialog = builder.show(); AlertDialog alertDialog = builder.show();
final Button positiveButton = final Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
input.setValidator(new AutoCompleteTextView.Validator() { input.setValidator(new AutoCompleteTextView.Validator() {
@Override @Override
public boolean isValid(CharSequence charSequence) { public boolean isValid(CharSequence charSequence) {
@ -408,16 +469,12 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
}); });
input.addTextChangedListener(new TextWatcherAdapter() { input.addTextChangedListener(new TextWatcherAdapter() {
@Override @Override
public void onTextChanged( public void onTextChanged(@NonNull CharSequence charSequence, int i, int i1, int i2) {
@NonNull CharSequence charSequence, int i, int i1, int i2) { positiveButton.setEnabled(customRepoManager.canAddRepo(charSequence.toString()) && customRepoManager.getRepoCount() < CUSTOM_REPO_ENTRIES);
positiveButton.setEnabled(customRepoManager
.canAddRepo(charSequence.toString()) &&
customRepoManager.getRepoCount() < CUSTOM_REPO_ENTRIES);
} }
}); });
positiveButton.setEnabled(false); positiveButton.setEnabled(false);
int dp10 = FoxDisplay.dpToPixel(10), int dp10 = FoxDisplay.dpToPixel(10), dp20 = FoxDisplay.dpToPixel(20);
dp20 = FoxDisplay.dpToPixel(20);
FoxViewCompat.setMargin(input, dp20, dp10, dp20, dp10); FoxViewCompat.setMargin(input, dp20, dp10, dp20, dp10);
return true; return true;
}); });
@ -426,8 +483,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
private void setRepoData(String url) { private void setRepoData(String url) {
final RepoData repoData = RepoManager.getINSTANCE().get(url); final RepoData repoData = RepoManager.getINSTANCE().get(url);
setRepoData(repoData, "pref_" + (repoData == null ? setRepoData(repoData, "pref_" + (repoData == null ? RepoManager.internalIdOfUrl(url) : repoData.getPreferenceId()));
RepoManager.internalIdOfUrl(url) : repoData.getPreferenceId()));
} }
private void setRepoData(final RepoData repoData, String preferenceName) { private void setRepoData(final RepoData repoData, String preferenceName) {
@ -442,11 +498,9 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
preference = findPreference(preferenceName + "_enabled"); preference = findPreference(preferenceName + "_enabled");
if (preference != null) { if (preference != null) {
((TwoStatePreference) preference).setChecked(repoData.isEnabled()); ((TwoStatePreference) preference).setChecked(repoData.isEnabled());
preference.setTitle(repoData.isEnabled() ? preference.setTitle(repoData.isEnabled() ? R.string.repo_enabled : R.string.repo_disabled);
R.string.repo_enabled : R.string.repo_disabled);
preference.setOnPreferenceChangeListener((p, newValue) -> { preference.setOnPreferenceChangeListener((p, newValue) -> {
p.setTitle(((Boolean) newValue) ? p.setTitle(((Boolean) newValue) ? R.string.repo_enabled : R.string.repo_disabled);
R.string.repo_enabled : R.string.repo_disabled);
return true; return true;
}); });
} }
@ -457,8 +511,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
preference.setVisible(true); preference.setVisible(true);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
if (homepage.startsWith("https://www.androidacy.com/")) { if (homepage.startsWith("https://www.androidacy.com/")) {
IntentHelper.openUrlAndroidacy( IntentHelper.openUrlAndroidacy(getFoxActivity(this), homepage, true);
getFoxActivity(this), homepage, true);
} else { } else {
IntentHelper.openUrl(getFoxActivity(this), homepage); IntentHelper.openUrl(getFoxActivity(this), homepage);
} }
@ -503,8 +556,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
preference.setVisible(true); preference.setVisible(true);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
if (submissionUrl.startsWith("https://www.androidacy.com/")) { if (submissionUrl.startsWith("https://www.androidacy.com/")) {
IntentHelper.openUrlAndroidacy( IntentHelper.openUrlAndroidacy(getFoxActivity(this), submissionUrl, true);
getFoxActivity(this), submissionUrl, true);
} else { } else {
IntentHelper.openUrl(getFoxActivity(this), submissionUrl); IntentHelper.openUrl(getFoxActivity(this), submissionUrl);
} }

@ -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="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>

@ -92,4 +92,6 @@
<string name="enable_blur_pref">Povolit rozostření</string> <string name="enable_blur_pref">Povolit rozostření</string>
<string name="repo_enabled">Repozitář povolen</string> <string name="repo_enabled">Repozitář povolen</string>
<string name="repo_disabled">Repozitář zakázán</string> <string name="repo_disabled">Repozitář zakázán</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -94,7 +94,6 @@
<string name="add_repo">Repo hinzufügen</string> <string name="add_repo">Repo hinzufügen</string>
<string name="remove_repo">Repo entfernen</string> <string name="remove_repo">Repo entfernen</string>
<string name="custom_url">Eigene URL</string> <string name="custom_url">Eigene URL</string>
<string name="androidacy_repo_info">Das Androidacy-Repo enthält Anzeigen und Tracker.</string>
<string name="backup_module_list">Backup Modules</string> <string name="backup_module_list">Backup Modules</string>
<string name="restore_module_list">Module wiederherstellen</string> <string name="restore_module_list">Module wiederherstellen</string>
<string name="require_internet">Dieser Vorgang erfordert eine Internetverbindung</string> <string name="require_internet">Dieser Vorgang erfordert eine Internetverbindung</string>
@ -103,4 +102,6 @@
<string name="description">Beschreibung</string> <string name="description">Beschreibung</string>
<string name="uninstall">Deinstallieren</string> <string name="uninstall">Deinstallieren</string>
<string name="config">Konfig</string> <string name="config">Konfig</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -124,7 +124,6 @@
<string name="add_repo">Προσθήκη αποθετηρίου</string> <string name="add_repo">Προσθήκη αποθετηρίου</string>
<string name="remove_repo">Αφαίρεση αποθετηρίου</string> <string name="remove_repo">Αφαίρεση αποθετηρίου</string>
<string name="custom_url">Προσαρμοσμένη διεύθυνση url</string> <string name="custom_url">Προσαρμοσμένη διεύθυνση url</string>
<string name="androidacy_repo_info">Το αποθετήριο Androidacy έχει διαφημίσεις και ιχνηλάτες.</string>
<string name="backup_module_list">Εφεδρικά modules</string> <string name="backup_module_list">Εφεδρικά modules</string>
<string name="restore_module_list">Επαναφορά module</string> <string name="restore_module_list">Επαναφορά module</string>
<string name="require_internet">Αυτή η λειτουργία απαιτεί σύνδεση στο Διαδίκτυο</string> <string name="require_internet">Αυτή η λειτουργία απαιτεί σύνδεση στο Διαδίκτυο</string>
@ -144,4 +143,6 @@
<string name="language_support_outdated">Ορισμένες μεταφράσεις για την τρέχουσα γλώσσα δεν είναι ενημερωμένες, παρακαλούμε να συνεισφέρετε στις μεταφράσεις εφαρμογών στο GitHub</string> <string name="language_support_outdated">Ορισμένες μεταφράσεις για την τρέχουσα γλώσσα δεν είναι ενημερωμένες, παρακαλούμε να συνεισφέρετε στις μεταφράσεις εφαρμογών στο GitHub</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">Μεταφράστηκε απο Tha_14</string> <string name="language_translated_by">Μεταφράστηκε απο Tha_14</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -124,8 +124,9 @@
<string name="add_repo">Añadir Repositorio</string> <string name="add_repo">Añadir Repositorio</string>
<string name="remove_repo">Eliminar Repositorio</string> <string name="remove_repo">Eliminar Repositorio</string>
<string name="custom_url">URL Personalizado</string> <string name="custom_url">URL Personalizado</string>
<string name="androidacy_repo_info">El repositorio Androidacy tiene anuncios y trackers.</string>
<string name="backup_module_list">Hacer copia de seguridad de los módulos</string> <string name="backup_module_list">Hacer copia de seguridad de los módulos</string>
<string name="restore_module_list">Restaurar módulos</string> <string name="restore_module_list">Restaurar módulos</string>
<string name="require_internet">Esta operación requiere una conexión a Internet.</string> <string name="require_internet">Esta operación requiere una conexión a Internet.</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -87,4 +87,6 @@
</string> </string>
<string name="repo_enabled">Hoidla lubatud</string> <string name="repo_enabled">Hoidla lubatud</string>
<string name="repo_disabled">Hoidla keelatud</string> <string name="repo_disabled">Hoidla keelatud</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -124,8 +124,9 @@
<string name="add_repo">Ajouter dépôt</string> <string name="add_repo">Ajouter dépôt</string>
<string name="remove_repo">Supprimer dépôt</string> <string name="remove_repo">Supprimer dépôt</string>
<string name="custom_url">URL personnalisée</string> <string name="custom_url">URL personnalisée</string>
<string name="androidacy_repo_info">Le dépôt Androidacy utilise des publicités et des pisteurs.</string>
<string name="backup_module_list">Sauvegarder les modules</string> <string name="backup_module_list">Sauvegarder les modules</string>
<string name="restore_module_list">Restaurer les modules</string> <string name="restore_module_list">Restaurer les modules</string>
<string name="require_internet">Cette opération requière une connexion internet</string> <string name="require_internet">Cette opération requière une connexion internet</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -93,4 +93,6 @@
<string name="enable_blur_pref">Aktifkan blur</string> <string name="enable_blur_pref">Aktifkan blur</string>
<string name="repo_enabled">Repo diaktifkan</string> <string name="repo_enabled">Repo diaktifkan</string>
<string name="repo_disabled">Repo dinonaktifkan</string> <string name="repo_disabled">Repo dinonaktifkan</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -127,7 +127,6 @@
<string name="add_repo">Aggiungi Repository</string> <string name="add_repo">Aggiungi Repository</string>
<string name="remove_repo">Rimuovi Repository</string> <string name="remove_repo">Rimuovi Repository</string>
<string name="custom_url">Url personalizzato</string> <string name="custom_url">Url personalizzato</string>
<string name="androidacy_repo_info">La repository di Androidacy contiene pubblicità e tracker.</string>
<string name="backup_module_list">Salva moduli</string> <string name="backup_module_list">Salva moduli</string>
<string name="restore_module_list">Ripristina moduli</string> <string name="restore_module_list">Ripristina moduli</string>
<string name="require_internet">Questa operazione richiede una connessione ad internet</string> <string name="require_internet">Questa operazione richiede una connessione ad internet</string>
@ -149,4 +148,6 @@
<!-- Sostituisci con il tuo nome utente durante la traduzione --> <!-- Sostituisci con il tuo nome utente durante la traduzione -->
<string name="language_translated_by">Traduzione di tugaia56</string> <string name="language_translated_by">Traduzione di tugaia56</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -40,4 +40,6 @@
<string name="source_code">ソースコード</string> <string name="source_code">ソースコード</string>
<string name="dev_mode_enabled">開発者モードが有効です</string> <string name="dev_mode_enabled">開発者モードが有効です</string>
<string name="force_english_pref">Force English language</string> <string name="force_english_pref">Force English language</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -94,5 +94,6 @@
<string name="enable_blur_pref">Tilsløring</string> <string name="enable_blur_pref">Tilsløring</string>
<string name="repo_enabled">Pakkebrønn på</string> <string name="repo_enabled">Pakkebrønn på</string>
<string name="repo_disabled">Pakkebrønn av</string> <string name="repo_disabled">Pakkebrønn av</string>
<string name="androidacy_repo_info">Androidacy-pakkebrønnen har reklame og sporere.</string> <string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources>

@ -124,7 +124,6 @@
<string name="add_repo">Dodaj repozytorium</string> <string name="add_repo">Dodaj repozytorium</string>
<string name="remove_repo">Usuń repozytorium</string> <string name="remove_repo">Usuń repozytorium</string>
<string name="custom_url">Niestandardowy URL</string> <string name="custom_url">Niestandardowy URL</string>
<string name="androidacy_repo_info">Repo Androidacy korzysta z reklam i trackerów.</string>
<string name="backup_module_list">Kopia zapasowa modułów</string> <string name="backup_module_list">Kopia zapasowa modułów</string>
<string name="restore_module_list">Przywracanie modułów</string> <string name="restore_module_list">Przywracanie modułów</string>
<string name="require_internet">Ta operacja wymaga połączenia internetowego</string> <string name="require_internet">Ta operacja wymaga połączenia internetowego</string>
@ -147,4 +146,6 @@
prosimy o rozważenie uzupełnienia tłumaczeń aplikacji na GitHubie.</string> prosimy o rozważenie uzupełnienia tłumaczeń aplikacji na GitHubie.</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">Tłumaczenie: Daviteusz</string> <string name="language_translated_by">Tłumaczenie: Daviteusz</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -127,7 +127,6 @@
<string name="add_repo">Adicionar Repositório</string> <string name="add_repo">Adicionar Repositório</string>
<string name="remove_repo">Remover Repositórios</string> <string name="remove_repo">Remover Repositórios</string>
<string name="custom_url">URL Customizado</string> <string name="custom_url">URL Customizado</string>
<string name="androidacy_repo_info">O Repositórios Androidacy possui anuncios e rastreadores.</string>
<string name="backup_module_list">Fazer backup dos modulos</string> <string name="backup_module_list">Fazer backup dos modulos</string>
<string name="restore_module_list">Restaurar modulos</string> <string name="restore_module_list">Restaurar modulos</string>
<string name="require_internet">Essa operação requer uma conexão com a internet</string> <string name="require_internet">Essa operação requer uma conexão com a internet</string>
@ -149,4 +148,6 @@
<string name="language_support_outdated">Algumas traduções para sua lingua atual não esta em dia, por favor considere contribuir para as traduções do app no Github</string> <string name="language_support_outdated">Algumas traduções para sua lingua atual não esta em dia, por favor considere contribuir para as traduções do app no Github</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">Traduzido por mi007d e DanGLES3</string> <string name="language_translated_by">Traduzido por mi007d e DanGLES3</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -119,7 +119,6 @@
<string name="add_repo">Adăugare depozit</string> <string name="add_repo">Adăugare depozit</string>
<string name="remove_repo">Eliminare depozit</string> <string name="remove_repo">Eliminare depozit</string>
<string name="custom_url">URL personalizat</string> <string name="custom_url">URL personalizat</string>
<string name="androidacy_repo_info">Depozitul Androidacy utilizează reclame și instrumente de urmărire.</string>
<string name="backup_module_list">Rezervare module</string> <string name="backup_module_list">Rezervare module</string>
<string name="restore_module_list">Restabilire module</string> <string name="restore_module_list">Restabilire module</string>
<string name="require_internet">Această operațiune necesită o conexiune la Internet</string> <string name="require_internet">Această operațiune necesită o conexiune la Internet</string>
@ -138,4 +137,6 @@
nu sunt actualizate, vă rugăm să luați în considerare contribuția la traducerea aplicației pe GitHub</string> nu sunt actualizate, vă rugăm să luați în considerare contribuția la traducerea aplicației pe GitHub</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">Tradus de Fox2Code</string> <string name="language_translated_by">Tradus de Fox2Code</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -120,8 +120,9 @@
<string name="add_repo">Добавить репо</string> <string name="add_repo">Добавить репо</string>
<string name="remove_repo">Убрать репо</string> <string name="remove_repo">Убрать репо</string>
<string name="custom_url">Свой URL</string> <string name="custom_url">Свой URL</string>
<string name="androidacy_repo_info">Репо Androidacy содержит рекламу и трекеры.</string>
<string name="backup_module_list">Резервное копирование модулей</string> <string name="backup_module_list">Резервное копирование модулей</string>
<string name="restore_module_list">Восстановить модули</string> <string name="restore_module_list">Восстановить модули</string>
<string name="require_internet">Для этого действия потребуется подключение к Интернету</string> <string name="require_internet">Для этого действия потребуется подключение к Интернету</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -92,4 +92,6 @@
<string name="enable_blur_pref">Povoliť rozostrenia</string> <string name="enable_blur_pref">Povoliť rozostrenia</string>
<string name="repo_enabled">Repozitár povolený</string> <string name="repo_enabled">Repozitár povolený</string>
<string name="repo_disabled">Repozitár zakázaný</string> <string name="repo_disabled">Repozitár zakázaný</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -127,7 +127,6 @@
<string name="add_repo">Repo ekle</string> <string name="add_repo">Repo ekle</string>
<string name="remove_repo">Repo\'yu kaldır</string> <string name="remove_repo">Repo\'yu kaldır</string>
<string name="custom_url">Özel url</string> <string name="custom_url">Özel url</string>
<string name="androidacy_repo_info">Androidacy repo\'da reklamlar ve takipçiler vardır.</string>
<string name="backup_module_list">Modülleri yedekle</string> <string name="backup_module_list">Modülleri yedekle</string>
<string name="restore_module_list">Modülleri geri yükle</string> <string name="restore_module_list">Modülleri geri yükle</string>
<string name="require_internet">Bu işlem bir internet bağlantısı gerektirir</string> <string name="require_internet">Bu işlem bir internet bağlantısı gerektirir</string>
@ -147,4 +146,6 @@
<string name="language_support_outdated">Şimdiki dil için bazı çeviriler güncel değil, lütfen GitHub\'daki uygulama çevirilerine katkıda bulunmayı düşünün</string> <string name="language_support_outdated">Şimdiki dil için bazı çeviriler güncel değil, lütfen GitHub\'daki uygulama çevirilerine katkıda bulunmayı düşünün</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">Alprnn357 tarafından çevrildi</string> <string name="language_translated_by">Alprnn357 tarafından çevrildi</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources>

@ -119,8 +119,9 @@
<string name="add_repo">Thêm kho</string> <string name="add_repo">Thêm kho</string>
<string name="remove_repo">Gỡ kho</string> <string name="remove_repo">Gỡ kho</string>
<string name="custom_url">Url tuỳ chỉnh</string> <string name="custom_url">Url tuỳ chỉnh</string>
<string name="androidacy_repo_info">Kho Androidacy có các quảng cáo và trình theo dõi.</string>
<string name="backup_module_list">Sao lưu mô-đun</string> <string name="backup_module_list">Sao lưu mô-đun</string>
<string name="restore_module_list">Khôi phục mô-đun</string> <string name="restore_module_list">Khôi phục mô-đun</string>
<string name="require_internet">Quá trình này yêu cầu kết nối internet</string> <string name="require_internet">Quá trình này yêu cầu kết nối internet</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -129,7 +129,6 @@
<string name="add_repo">添加库</string> <string name="add_repo">添加库</string>
<string name="remove_repo">移除库</string> <string name="remove_repo">移除库</string>
<string name="custom_url">自定义网址</string> <string name="custom_url">自定义网址</string>
<string name="androidacy_repo_info">Androidacy repo具有广告和跟踪器.</string>
<string name="backup_module_list">备份模块</string> <string name="backup_module_list">备份模块</string>
<string name="restore_module_list">还原模块</string> <string name="restore_module_list">还原模块</string>
<string name="require_internet">此操作需要互联网连接</string> <string name="require_internet">此操作需要互联网连接</string>
@ -150,4 +149,6 @@
请考虑为 GitHub 上的应用程序翻译做出贡献</string> 请考虑为 GitHub 上的应用程序翻译做出贡献</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">由ender-zhao翻译</string> <string name="language_translated_by">由ender-zhao翻译</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -120,7 +120,6 @@
<string name="add_repo">新增倉庫</string> <string name="add_repo">新增倉庫</string>
<string name="remove_repo">移除倉庫</string> <string name="remove_repo">移除倉庫</string>
<string name="custom_url">自定義網址</string> <string name="custom_url">自定義網址</string>
<string name="androidacy_repo_info">Androidacy repo 內建廣告和跟蹤器.</string>
<string name="backup_module_list">備份模組</string> <string name="backup_module_list">備份模組</string>
<string name="restore_module_list">還原模組</string> <string name="restore_module_list">還原模組</string>
<string name="require_internet">需要網路連線</string> <string name="require_internet">需要網路連線</string>
@ -140,4 +139,6 @@
<string name="language_support_outdated">如果您使用的當前語言沒有及時更新,請考慮在 GitHub 上的為翻譯做出貢獻。</string> <string name="language_support_outdated">如果您使用的當前語言沒有及時更新,請考慮在 GitHub 上的為翻譯做出貢獻。</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">由 OrStudio.tw(@crcky5322) 提供美妙的翻譯</string> <string name="language_translated_by">由 OrStudio.tw(@crcky5322) 提供美妙的翻譯</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
</resources> </resources>

@ -24,4 +24,5 @@
<color name="light_colorBackgroundFloating">#FFFFFFFF</color> <color name="light_colorBackgroundFloating">#FFFFFFFF</color>
<color name="light_backgroundColor">#FFFFFFFF</color> <color name="light_backgroundColor">#FFFFFFFF</color>
<color name="light_chipBackgroundColor">#FFDADADA</color> <color name="light_chipBackgroundColor">#FFDADADA</color>
<color name="gray_900">#3A3A3A</color>
</resources> </resources>

@ -129,7 +129,7 @@
<string name="add_repo">Add Repo</string> <string name="add_repo">Add Repo</string>
<string name="remove_repo">Remove Repo</string> <string name="remove_repo">Remove Repo</string>
<string name="custom_url">Custom url</string> <string name="custom_url">Custom url</string>
<string name="androidacy_repo_info">The Androidacy repo has ads and trackers.</string> <string name="androidacy_repo_info">This repository may display some non-intrusive advertising to cover server and development costs.</string>
<string name="backup_module_list">Backup modules</string> <string name="backup_module_list">Backup modules</string>
<string name="restore_module_list">Restore modules</string> <string name="restore_module_list">Restore modules</string>
<string name="require_internet">This operation require an internet connection</string> <string name="require_internet">This operation require an internet connection</string>
@ -152,4 +152,20 @@
not up-to-date, please consider contributing to the app translations on GitHub</string> not up-to-date, please consider contributing to the app translations on GitHub</string>
<!-- Replace with your own username when translating --> <!-- Replace with your own username when translating -->
<string name="language_translated_by">Translated by Fox2Code</string> <string name="language_translated_by">Translated by Fox2Code</string>
<string name="crash_reporting">Automatically report bugs and performance to the developers</string>
<string name="crash_reporting_desc">If you disable this, the developer will not get automatic bug reports, and this may make troubleshooting harder</string>
<string name="api_key">Androidacy API Key</string>
<string name="api_key_summary">Use a custom API key for Androidacy. Useful for premium subscribers, to remove ads and more.</string>
<string name="api_key_not_set">Androidacy API key is empty</string>
<string name="api_key_set">Current Androidacy API key</string>
<string name="api_key_invalid">Could not validate API key. Please check it and try again.</string>
<string name="api_key_valid">API key is valid.</string>
<string name="checking_api_key">Validating API key...</string>
<string name="validating_api_key">Validating API key...</string>
<string name="please_wait">Please wait</string>
<string name="api_key_removed">Successfully reset API key</string>
<string name="save_api_key">Validate</string>
<string name="warning">Warning!</string>
<string name="androidacy_test_mode_warning">You are setting the app to use a non-production endpoint for Androidacy. This may result in app instability and failure to load the online repo. Do NOT report bugs if you have this switch on. Change will take effect on app restart.</string>
<string name="crash">Crash the app for testing</string>
</resources> </resources>

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"> <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory <PreferenceCategory
app:key="pref_magisk_alt_repo" app:key="pref_magisk_alt_repo"
app:title="@string/loading"> app:title="@string/loading">
@ -39,6 +40,27 @@
app:switchTextOn="@string/repo_enabled" app:switchTextOn="@string/repo_enabled"
app:switchTextOff="@string/repo_disabled" app:switchTextOff="@string/repo_disabled"
app:singleLineTitle="false" /> app:singleLineTitle="false" />
<!-- Allow user to set custom API key for Androidacy repo -->
<EditTextPreference
app:key="pref_androidacy_repo_api_key"
app:icon="@drawable/ic_baseline_vpn_key_24"
app:title="@string/api_key"
app:summary="@string/api_key_summary"
app:singleLineTitle="true"
app:dialogTitle="@string/api_key"
app:dialogIcon="@drawable/ic_baseline_vpn_key_24"
app:dependency="pref_androidacy_repo_enabled"
android:inputType="text"
android:selectAllOnFocus="true"
android:imeOptions="actionDone" />
<SwitchPreferenceCompat
app:defaultValue="false"
app:key="pref_androidacy_test_mode"
app:icon="@drawable/ic_baseline_bug_report_24"
app:title="@string/androidacy_test_mode_pref"
app:summary="@string/androidacy_test_mode_desc"
app:dependency="pref_androidacy_repo_enabled"
app:singleLineTitle="false" />
<Preference <Preference
app:key="pref_androidacy_repo_website" app:key="pref_androidacy_repo_website"
app:icon="@drawable/ic_baseline_language_24" app:icon="@drawable/ic_baseline_language_24"
@ -59,13 +81,6 @@
app:icon="@drawable/ic_baseline_upload_file_24" app:icon="@drawable/ic_baseline_upload_file_24"
app:title="@string/submit_modules" app:title="@string/submit_modules"
app:singleLineTitle="false" /> app:singleLineTitle="false" />
<SwitchPreferenceCompat
app:defaultValue="false"
app:key="pref_androidacy_test_mode"
app:icon="@drawable/ic_baseline_bug_report_24"
app:title="@string/androidacy_test_mode_pref"
app:summary="@string/androidacy_test_mode_desc"
app:singleLineTitle="false" />
<Preference <Preference
app:key="pref_androidacy_ads_disclaimer" app:key="pref_androidacy_ads_disclaimer"
app:icon="@drawable/ic_baseline_info_24" app:icon="@drawable/ic_baseline_info_24"

@ -3,152 +3,165 @@
<!-- Custom repos has been announced, check https://github.com/Fox2Code/FoxMagiskModuleManager/issues/131 --> <!-- Custom repos has been announced, check https://github.com/Fox2Code/FoxMagiskModuleManager/issues/131 -->
<PreferenceCategory app:title="@string/pref_category_repos"> <PreferenceCategory app:title="@string/pref_category_repos">
<Preference <Preference
app:key="pref_manage_repos"
app:icon="@drawable/ic_baseline_extension_24" app:icon="@drawable/ic_baseline_extension_24"
app:title="@string/manage_repos_pref" app:key="pref_manage_repos"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/manage_repos_pref" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_show_incompatible"
app:icon="@drawable/ic_baseline_hide_source_24" app:icon="@drawable/ic_baseline_hide_source_24"
app:title="@string/show_incompatible_pref" app:key="pref_show_incompatible"
app:singleLineTitle="false"
app:summary="@string/show_incompatible_desc" app:summary="@string/show_incompatible_desc"
app:singleLineTitle="false" /> app:title="@string/show_incompatible_pref" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_disable_low_quality_module_filter"
app:icon="@drawable/ic_baseline_warning_24" app:icon="@drawable/ic_baseline_warning_24"
app:title="@string/disable_low_quality_module_filter_pref" app:key="pref_disable_low_quality_module_filter"
app:singleLineTitle="false"
app:summary="@string/disable_low_quality_module_filter_desc" app:summary="@string/disable_low_quality_module_filter_desc"
app:singleLineTitle="false" /> app:title="@string/disable_low_quality_module_filter_pref" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_use_magisk_install_command"
app:icon="@drawable/ic_baseline_numbers_24" app:icon="@drawable/ic_baseline_numbers_24"
app:title="@string/use_magisk_install_command_pref" app:key="pref_use_magisk_install_command"
app:singleLineTitle="false"
app:summary="@string/use_magisk_install_command_desc" app:summary="@string/use_magisk_install_command_desc"
app:singleLineTitle="false" /> app:title="@string/use_magisk_install_command_pref" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="true"
app:key="pref_background_update_check"
app:icon="@drawable/ic_baseline_notifications_24" app:icon="@drawable/ic_baseline_notifications_24"
app:title="@string/notification_update_pref" app:key="pref_background_update_check"
app:singleLineTitle="false"
app:summary="@string/notification_update_desc" app:summary="@string/notification_update_desc"
app:singleLineTitle="false" /> app:title="@string/notification_update_pref" />
<Preference <Preference
app:key="pref_background_update_check_debug" app:key="pref_background_update_check_debug"
app:title="@string/notification_update_debug_pref" app:singleLineTitle="false"
app:singleLineTitle="false" /> app:title="@string/notification_update_debug_pref" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory app:title="@string/pref_category_appearance"> <PreferenceCategory app:title="@string/pref_category_appearance">
<ListPreference <ListPreference
app:key="pref_theme"
app:icon="@drawable/ic_baseline_palette_24"
app:title="@string/theme_pref"
app:defaultValue="system" app:defaultValue="system"
app:entries="@array/theme_values_names" app:entries="@array/theme_values_names"
app:entryValues="@array/theme_values" app:entryValues="@array/theme_values"
app:singleLineTitle="false" /> app:icon="@drawable/ic_baseline_palette_24"
app:key="pref_theme"
app:singleLineTitle="false"
app:title="@string/theme_pref" />
<Preference <Preference
app:key="pref_language_selector"
app:icon="@drawable/ic_baseline_language_24" app:icon="@drawable/ic_baseline_language_24"
app:title="@string/language" /> app:key="pref_language_selector"
app:title="@string/language" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_enable_blur"
app:icon="@drawable/ic_baseline_blur_on_24" app:icon="@drawable/ic_baseline_blur_on_24"
app:title="@string/enable_blur_pref" app:key="pref_enable_blur"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/enable_blur_pref" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_force_dark_terminal"
app:icon="@drawable/ic_baseline_list_24" app:icon="@drawable/ic_baseline_list_24"
app:title="@string/force_dark_terminal_title" app:key="pref_force_dark_terminal"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/force_dark_terminal_title" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="@bool/monet_enabled_by_default" app:defaultValue="@bool/monet_enabled_by_default"
app:key="pref_enable_monet"
app:icon="@drawable/ic_baseline_design_services_24" app:icon="@drawable/ic_baseline_design_services_24"
app:title="@string/enable_monet" app:key="pref_enable_monet"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/enable_monet" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_wrap_text"
app:icon="@drawable/ic_baseline_keyboard_return_24" app:icon="@drawable/ic_baseline_keyboard_return_24"
app:title="@string/wrap_text_pref" app:key="pref_wrap_text"
app:singleLineTitle="false"
app:summary="@string/wrap_text_desc" app:summary="@string/wrap_text_desc"
app:singleLineTitle="false" /> app:title="@string/wrap_text_pref" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory app:title="@string/pref_category_security"> <PreferenceCategory app:title="@string/pref_category_security">
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="true"
app:key="pref_dns_over_https"
app:icon="@drawable/ic_baseline_security_24" app:icon="@drawable/ic_baseline_security_24"
app:title="@string/dns_over_https_pref" app:key="pref_dns_over_https"
app:singleLineTitle="false"
app:summary="@string/dns_over_https_desc" app:summary="@string/dns_over_https_desc"
app:singleLineTitle="false" /> app:title="@string/dns_over_https_pref" />
<!-- Note: Lockdown mode used to be called showcase mode --> <!-- Note: Lockdown mode used to be called showcase mode -->
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:key="pref_showcase_mode"
app:icon="@drawable/ic_baseline_lock_24" app:icon="@drawable/ic_baseline_lock_24"
app:title="@string/showcase_mode_pref" app:key="pref_showcase_mode"
app:singleLineTitle="false"
app:summary="@string/showcase_mode_desc" app:summary="@string/showcase_mode_desc"
app:singleLineTitle="false" /> app:title="@string/showcase_mode_pref" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="true"
app:key="pref_prevent_reboot"
app:icon="@drawable/ic_reboot_24" app:icon="@drawable/ic_reboot_24"
app:title="@string/prevent_reboot_pref" app:key="pref_prevent_reboot"
app:singleLineTitle="false"
app:summary="@string/prevent_reboot_desc" app:summary="@string/prevent_reboot_desc"
app:singleLineTitle="false" /> app:title="@string/prevent_reboot_pref" />
<!-- Crash reporting -->
<SwitchPreferenceCompat
app:defaultValue="true"
app:icon="@drawable/ic_baseline_bug_report_24"
app:key="pref_crash_reporting"
app:singleLineTitle="false"
app:summary="@string/crash_reporting_desc"
app:title="@string/crash_reporting" />
<!-- Purposely crash the app -->
<Preference
app:icon="@drawable/ic_baseline_bug_report_24"
app:key="pref_crash"
app:singleLineTitle="false"
app:title="@string/crash" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory app:title="@string/pref_category_info">
app:title="@string/pref_category_info">
<Preference <Preference
app:key="pref_update"
app:icon="@drawable/ic_baseline_system_update_24" app:icon="@drawable/ic_baseline_system_update_24"
app:title="@string/app_update" app:key="pref_update"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/app_update" />
<Preference <Preference
app:key="pref_report_bug"
app:icon="@drawable/ic_baseline_bug_report_24" app:icon="@drawable/ic_baseline_bug_report_24"
app:title="@string/report_bugs" app:key="pref_report_bug"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/report_bugs" />
<Preference <Preference
app:key="pref_source_code"
app:icon="@drawable/ic_github" app:icon="@drawable/ic_github"
app:title="@string/source_code" app:key="pref_source_code"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/source_code" />
<Preference <Preference
app:key="pref_support"
app:icon="@drawable/ic_baseline_telegram_24" app:icon="@drawable/ic_baseline_telegram_24"
app:title="@string/support" app:key="pref_support"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/support" />
<Preference <Preference
app:key="pref_show_licenses"
app:icon="@drawable/ic_baseline_info_24" app:icon="@drawable/ic_baseline_info_24"
app:title="@string/show_licenses" app:key="pref_show_licenses"
app:singleLineTitle="false" /> app:singleLineTitle="false"
app:title="@string/show_licenses" />
<Preference <Preference
app:key="pref_pkg_info"
app:enabled="false" app:enabled="false"
app:summary="@string/loading" app:iconSpaceReserved="false"
app:key="pref_pkg_info"
app:singleLineTitle="false" app:singleLineTitle="false"
app:iconSpaceReserved="false" /> app:summary="@string/loading" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

@ -5,7 +5,7 @@ buildscript {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
} }
project.ext.latestAboutLibsRelease = "10.3.1" project.ext.latestAboutLibsRelease = "10.4.1-a01"
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.2' classpath 'com.android.tools.build:gradle:7.2.2'
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}"

Loading…
Cancel
Save