and the kotlin keeps coming
just keeps on coming Signed-off-by: androidacy-user <opensource@androidacy.com>pull/27/head
parent
a73a7b613e
commit
7068288d07
@ -1,138 +1,139 @@
|
||||
package com.fox2code.mmm.androidacy;
|
||||
@file:Suppress("unused")
|
||||
|
||||
import android.net.Uri;
|
||||
package com.fox2code.mmm.androidacy
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.net.Uri
|
||||
import com.fox2code.mmm.BuildConfig
|
||||
import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
|
||||
import java.io.IOException
|
||||
|
||||
import com.fox2code.mmm.BuildConfig;
|
||||
import com.fox2code.mmm.utils.io.net.Http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public enum AndroidacyUtil {
|
||||
enum class AndroidacyUtil {
|
||||
;
|
||||
public static final String REFERRER = "utm_source=FoxMMM&utm_medium=app";
|
||||
|
||||
public static boolean isAndroidacyLink(@Nullable Uri uri) {
|
||||
return uri != null && isAndroidacyLink(uri.toString(), uri);
|
||||
}
|
||||
|
||||
public static boolean isAndroidacyLink(@Nullable String url) {
|
||||
return url != null && isAndroidacyLink(url, Uri.parse(url));
|
||||
}
|
||||
|
||||
static boolean isAndroidacyLink(@NonNull String url, @NonNull Uri uri) {
|
||||
int i; // Check both string and Uri to mitigate parse exploit
|
||||
return url.startsWith("https://") && (i = url.indexOf("/", 8)) != -1 && url.substring(8, i).endsWith("api.androidacy.com") && Objects.requireNonNull(uri.getHost()).endsWith("api.androidacy.com");
|
||||
}
|
||||
companion object {
|
||||
const val REFERRER = "utm_source=FoxMMM&utm_medium=app"
|
||||
fun isAndroidacyLink(uri: Uri?): Boolean {
|
||||
return uri != null && isAndroidacyLink(uri.toString(), uri)
|
||||
}
|
||||
|
||||
public static boolean isAndroidacyFileUrl(@Nullable String url) {
|
||||
if (url == null)
|
||||
return false;
|
||||
for (String prefix : new String[]{"https://production-api.androidacy.com/downloads/", "https://production-api.androidacy.com/magisk/file/", "https://staging-api.androidacy.com/magisk/file/"}) { // Make both staging and non staging act the same
|
||||
if (url.startsWith(prefix))
|
||||
return true;
|
||||
fun isAndroidacyLink(url: String?): Boolean {
|
||||
return url != null && isAndroidacyLink(url, Uri.parse(url))
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid logging token
|
||||
public static String hideToken(@NonNull String url) {
|
||||
// for token, device_id, and client_id, replace with <hidden> by using replaceAll to match until the next non-alphanumeric character or end
|
||||
// Also, URL decode
|
||||
url = Uri.decode(url);
|
||||
url = url + "&";
|
||||
url = url.replaceAll("token=[^&]*", "token=<hidden>");
|
||||
url = url.replaceAll("device_id=[^&]*", "device_id=<hidden>");
|
||||
url = url.replaceAll("client_id=[^&]*", "client_id=<hidden>");
|
||||
// remove last & added at the end
|
||||
url = url.substring(0, url.length() - 1);
|
||||
return url;
|
||||
}
|
||||
fun isAndroidacyLink(url: String, uri: Uri): Boolean {
|
||||
var i = 0// Check both string and Uri to mitigate parse exploit
|
||||
return url.startsWith("https://") && url.indexOf("/", 8)
|
||||
.also { i = it } != -1 && url.substring(8, i)
|
||||
.endsWith("api.androidacy.com") && uri.host?.endsWith("api.androidacy.com") ?: false
|
||||
}
|
||||
|
||||
public static String getModuleId(String moduleUrl) {
|
||||
// Get the &module= part
|
||||
int i = moduleUrl.indexOf("&module=");
|
||||
String moduleId;
|
||||
// Match until next & or end
|
||||
if (i != -1) {
|
||||
int j = moduleUrl.indexOf('&', i + 1);
|
||||
if (j == -1) {
|
||||
moduleId = moduleUrl.substring(i + 8);
|
||||
} else {
|
||||
moduleId = moduleUrl.substring(i + 8, j);
|
||||
@JvmStatic
|
||||
fun isAndroidacyFileUrl(url: String?): Boolean {
|
||||
if (url == null) return false
|
||||
for (prefix in arrayOf(
|
||||
"https://production-api.androidacy.com/downloads/",
|
||||
"https://production-api.androidacy.com/magisk/file/",
|
||||
"https://staging-api.androidacy.com/magisk/file/"
|
||||
)) { // Make both staging and non staging act the same
|
||||
if (url.startsWith(prefix)) return true
|
||||
}
|
||||
// URL decode
|
||||
moduleId = Uri.decode(moduleId);
|
||||
// Strip non alphanumeric
|
||||
moduleId = moduleId.replaceAll("[^a-zA-Z\\d]", "");
|
||||
return moduleId;
|
||||
return false
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw new IllegalArgumentException("Invalid module url: " + moduleUrl);
|
||||
|
||||
// Avoid logging token
|
||||
@Suppress("NAME_SHADOWING")
|
||||
@JvmStatic
|
||||
fun hideToken(url: String): String {
|
||||
// for token, device_id, and client_id, replace with <hidden> by using replaceAll to match until the next non-alphanumeric character or end
|
||||
// Also, URL decode
|
||||
var url = url
|
||||
url = Uri.decode(url)
|
||||
url = "$url&"
|
||||
url = url.replace("token=[^&]*".toRegex(), "token=<hidden>")
|
||||
url = url.replace("device_id=[^&]*".toRegex(), "device_id=<hidden>")
|
||||
url = url.replace("client_id=[^&]*".toRegex(), "client_id=<hidden>")
|
||||
// remove last & added at the end
|
||||
url = url.substring(0, url.length - 1)
|
||||
return url
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getModuleTitle(String moduleUrl) {
|
||||
// Get the &title= part
|
||||
int i = moduleUrl.indexOf("&moduleTitle=");
|
||||
// Match until next & or end
|
||||
if (i != -1) {
|
||||
int j = moduleUrl.indexOf('&', i + 1);
|
||||
if (j == -1) {
|
||||
return Uri.decode(moduleUrl.substring(i + 13));
|
||||
} else {
|
||||
return Uri.decode(moduleUrl.substring(i + 13, j));
|
||||
@JvmStatic
|
||||
fun getModuleId(moduleUrl: String): String? {
|
||||
// Get the &module= part
|
||||
val i = moduleUrl.indexOf("&module=")
|
||||
var moduleId: String
|
||||
// Match until next & or end
|
||||
if (i != -1) {
|
||||
val j = moduleUrl.indexOf('&', i + 1)
|
||||
moduleId = if (j == -1) {
|
||||
moduleUrl.substring(i + 8)
|
||||
} else {
|
||||
moduleUrl.substring(i + 8, j)
|
||||
}
|
||||
// URL decode
|
||||
moduleId = Uri.decode(moduleId)
|
||||
// Strip non alphanumeric
|
||||
moduleId = moduleId.replace("[^a-zA-Z\\d]".toRegex(), "")
|
||||
return moduleId
|
||||
}
|
||||
require(!BuildConfig.DEBUG) { "Invalid module url: $moduleUrl" }
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getChecksumFromURL(String moduleUrl) {
|
||||
// Get the &version= part
|
||||
int i = moduleUrl.indexOf("&checksum=");
|
||||
// Match until next & or end
|
||||
if (i != -1) {
|
||||
int j = moduleUrl.indexOf('&', i + 1);
|
||||
if (j == -1) {
|
||||
return moduleUrl.substring(i + 10);
|
||||
} else {
|
||||
return moduleUrl.substring(i + 10, j);
|
||||
@JvmStatic
|
||||
fun getModuleTitle(moduleUrl: String): String? {
|
||||
// Get the &title= part
|
||||
val i = moduleUrl.indexOf("&moduleTitle=")
|
||||
// Match until next & or end
|
||||
if (i != -1) {
|
||||
val j = moduleUrl.indexOf('&', i + 1)
|
||||
return if (j == -1) {
|
||||
Uri.decode(moduleUrl.substring(i + 13))
|
||||
} else {
|
||||
Uri.decode(moduleUrl.substring(i + 13, j))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the url is a premium direct download link
|
||||
* @param url url to check
|
||||
* @return true if it is a premium direct download link
|
||||
* @noinspection unused
|
||||
*/
|
||||
public static boolean isPremiumDirectDownloadLink(String url) {
|
||||
return url.contains("/magisk/ddl/");
|
||||
}
|
||||
fun getChecksumFromURL(moduleUrl: String): String? {
|
||||
// Get the &version= part
|
||||
val i = moduleUrl.indexOf("&checksum=")
|
||||
// Match until next & or end
|
||||
if (i != -1) {
|
||||
val j = moduleUrl.indexOf('&', i + 1)
|
||||
return if (j == -1) {
|
||||
moduleUrl.substring(i + 10)
|
||||
} else {
|
||||
moduleUrl.substring(i + 10, j)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the markdown directly from the API for rendering. Premium only, and internal testing only currently.
|
||||
* @param url URL to get markdown from
|
||||
* @return String of markdown
|
||||
* @noinspection unused
|
||||
*/
|
||||
public static String getMarkdownFromAPI(String url) {
|
||||
byte[] md;
|
||||
try {
|
||||
md = Http.doHttpGet(url, false);
|
||||
} catch (IOException ignored) {
|
||||
return null;
|
||||
/**
|
||||
* Check if the url is a premium direct download link
|
||||
* @param url url to check
|
||||
* @return true if it is a premium direct download link
|
||||
* @noinspection unused
|
||||
*/
|
||||
fun isPremiumDirectDownloadLink(url: String): Boolean {
|
||||
return url.contains("/magisk/ddl/")
|
||||
}
|
||||
if (md == null) {
|
||||
return null;
|
||||
|
||||
/**
|
||||
* Returns the markdown directly from the API for rendering. Premium only, and internal testing only currently.
|
||||
* @param url URL to get markdown from
|
||||
* @return String of markdown
|
||||
* @noinspection unused
|
||||
*/
|
||||
fun getMarkdownFromAPI(url: String?): String? {
|
||||
val md: ByteArray = try {
|
||||
doHttpGet(url!!, false)
|
||||
} catch (ignored: IOException) {
|
||||
return null
|
||||
}
|
||||
return String(md)
|
||||
}
|
||||
return new String(md);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +1,70 @@
|
||||
package com.fox2code.mmm.repo;
|
||||
package com.fox2code.mmm.repo
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.StringRes
|
||||
import com.fox2code.mmm.manager.ModuleInfo
|
||||
|
||||
import com.fox2code.mmm.manager.ModuleInfo;
|
||||
class RepoModule {
|
||||
@JvmField
|
||||
val repoData: RepoData
|
||||
@JvmField
|
||||
val moduleInfo: ModuleInfo
|
||||
@JvmField
|
||||
val id: String
|
||||
@JvmField
|
||||
var repoName: String? = null
|
||||
@JvmField
|
||||
var lastUpdated: Long = 0
|
||||
@JvmField
|
||||
var propUrl: String? = null
|
||||
@JvmField
|
||||
var zipUrl: String? = null
|
||||
@JvmField
|
||||
var notesUrl: String? = null
|
||||
@JvmField
|
||||
var checksum: String? = null
|
||||
@JvmField
|
||||
var processed = false
|
||||
|
||||
public class RepoModule {
|
||||
public final RepoData repoData;
|
||||
public final ModuleInfo moduleInfo;
|
||||
public final String id;
|
||||
public String repoName;
|
||||
public long lastUpdated;
|
||||
public String propUrl;
|
||||
public String zipUrl;
|
||||
public String notesUrl;
|
||||
public String checksum;
|
||||
public boolean processed;
|
||||
@JvmField
|
||||
@StringRes
|
||||
public int qualityText;
|
||||
public int qualityValue;
|
||||
public boolean safe;
|
||||
var qualityText = 0
|
||||
@JvmField
|
||||
var qualityValue = 0
|
||||
var safe: Boolean
|
||||
|
||||
public RepoModule(RepoData repoData, String id) {
|
||||
this.repoData = repoData;
|
||||
this.moduleInfo = new ModuleInfo(id);
|
||||
this.id = id;
|
||||
this.moduleInfo.flags |=
|
||||
ModuleInfo.FLAG_METADATA_INVALID;
|
||||
this.safe = this.moduleInfo.safe;
|
||||
constructor(repoData: RepoData, id: String) {
|
||||
this.repoData = repoData
|
||||
moduleInfo = ModuleInfo(id)
|
||||
this.id = id
|
||||
moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_METADATA_INVALID
|
||||
safe = moduleInfo.safe
|
||||
}
|
||||
|
||||
// allows all fields to be set-
|
||||
public RepoModule(RepoData repoData, String id, String name, String description, String author, String donate, String config, String support, String version, int versionCode) {
|
||||
this.repoData = repoData;
|
||||
this.moduleInfo = new ModuleInfo(id);
|
||||
this.id = id;
|
||||
this.moduleInfo.name = name;
|
||||
this.moduleInfo.description = description;
|
||||
this.moduleInfo.author = author;
|
||||
this.moduleInfo.donate = donate;
|
||||
this.moduleInfo.config = config;
|
||||
this.moduleInfo.support = support;
|
||||
this.moduleInfo.version = version;
|
||||
this.moduleInfo.versionCode = versionCode;
|
||||
this.moduleInfo.flags |=
|
||||
ModuleInfo.FLAG_METADATA_INVALID;
|
||||
this.safe = this.moduleInfo.safe;
|
||||
constructor(
|
||||
repoData: RepoData,
|
||||
id: String,
|
||||
name: String?,
|
||||
description: String?,
|
||||
author: String?,
|
||||
donate: String?,
|
||||
config: String?,
|
||||
support: String?,
|
||||
version: String?,
|
||||
versionCode: Int
|
||||
) {
|
||||
this.repoData = repoData
|
||||
moduleInfo = ModuleInfo(id)
|
||||
this.id = id
|
||||
moduleInfo.name = name
|
||||
moduleInfo.description = description
|
||||
moduleInfo.author = author
|
||||
moduleInfo.donate = donate
|
||||
moduleInfo.config = config
|
||||
moduleInfo.support = support
|
||||
moduleInfo.version = version
|
||||
moduleInfo.versionCode = versionCode.toLong()
|
||||
moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_METADATA_INVALID
|
||||
safe = moduleInfo.safe
|
||||
}
|
||||
}
|
||||
}
|
@ -1,423 +1,460 @@
|
||||
package com.fox2code.mmm.utils.io;
|
||||
@file:Suppress("unused")
|
||||
|
||||
import static com.fox2code.mmm.AppUpdateManager.FLAG_COMPAT_LOW_QUALITY;
|
||||
import static com.fox2code.mmm.AppUpdateManager.getFlagsForModule;
|
||||
package com.fox2code.mmm.utils.io
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import com.fox2code.mmm.AppUpdateManager
|
||||
import com.fox2code.mmm.manager.ModuleInfo
|
||||
import com.topjohnwu.superuser.io.SuFileInputStream
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import com.fox2code.mmm.AppUpdateManager;
|
||||
import com.fox2code.mmm.manager.ModuleInfo;
|
||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
enum class PropUtils {
|
||||
;
|
||||
|
||||
import timber.log.Timber;
|
||||
@Suppress("SpellCheckingInspection")
|
||||
companion object {
|
||||
private val moduleSupportsFallbacks = HashMap<String, String>()
|
||||
private val moduleConfigsFallbacks = HashMap<String, String>()
|
||||
private val moduleMinApiFallbacks = HashMap<String, Int>()
|
||||
private val moduleUpdateJsonFallbacks = HashMap<String, String>()
|
||||
private val moduleMTTRebornFallback = HashSet<String>()
|
||||
private val moduleImportantProp = HashSet(
|
||||
mutableListOf(
|
||||
"id", "name", "version", "versionCode"
|
||||
)
|
||||
)
|
||||
private var RIRU_MIN_API = 0
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public enum PropUtils {
|
||||
;
|
||||
private static final HashMap<String, String> moduleSupportsFallbacks = new HashMap<>();
|
||||
private static final HashMap<String, String> moduleConfigsFallbacks = new HashMap<>();
|
||||
private static final HashMap<String, Integer> moduleMinApiFallbacks = new HashMap<>();
|
||||
private static final HashMap<String, String> moduleUpdateJsonFallbacks = new HashMap<>();
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private static final HashSet<String> moduleMTTRebornFallback = new HashSet<>();
|
||||
private static final HashSet<String> moduleImportantProp = new HashSet<>(Arrays.asList(
|
||||
"id", "name", "version", "versionCode"
|
||||
));
|
||||
private static final int RIRU_MIN_API;
|
||||
|
||||
// Note: These fallback values may not be up-to-date
|
||||
// They are only used if modules don't define the metadata
|
||||
static {
|
||||
// Support are pages or groups where the user can get support for the module
|
||||
moduleSupportsFallbacks.put("aospill", "https://t.me/PannekoX");
|
||||
moduleSupportsFallbacks.put("bromitewebview", "https://t.me/androidacy_discussions");
|
||||
moduleSupportsFallbacks.put("fontrevival", "https://t.me/androidacy_discussions");
|
||||
moduleSupportsFallbacks.put("MagiskHidePropsConf", "https://forum.xda-developers.com/t" +
|
||||
"/module-magiskhide-props-config-safetynet-prop-edits-and-more-v6-1-1.3789228/");
|
||||
moduleSupportsFallbacks.put("quickstepswitcher", "https://t.me/QuickstepSwitcherSupport");
|
||||
moduleSupportsFallbacks.put("riru_edxposed", "https://t.me/EdXposed");
|
||||
moduleSupportsFallbacks.put("riru_lsposed", "https://github.com/LSPosed/LSPosed/issues");
|
||||
moduleSupportsFallbacks.put("substratum", "https://github.com/substratum/substratum/issues");
|
||||
// Config are application installed by modules that allow them to be configured
|
||||
moduleConfigsFallbacks.put("quickstepswitcher", "xyz.paphonb.quickstepswitcher");
|
||||
moduleConfigsFallbacks.put("hex_installer_module", "project.vivid.hex.bodhi");
|
||||
moduleConfigsFallbacks.put("riru_edxposed", "org.meowcat.edxposed.manager");
|
||||
moduleConfigsFallbacks.put("riru_lsposed", "org.lsposed.manager");
|
||||
moduleConfigsFallbacks.put("zygisk_lsposed", "org.lsposed.manager");
|
||||
moduleConfigsFallbacks.put("xposed_dalvik", "de.robv.android.xposed.installer");
|
||||
moduleConfigsFallbacks.put("xposed", "de.robv.android.xposed.installer");
|
||||
moduleConfigsFallbacks.put("substratum", "projekt.substratum");
|
||||
// minApi is the minimum android version required to use the module
|
||||
moduleMinApiFallbacks.put("HideNavBar", Build.VERSION_CODES.Q);
|
||||
moduleMinApiFallbacks.put("riru_ifw_enhance", Build.VERSION_CODES.O);
|
||||
moduleMinApiFallbacks.put("zygisk_ifw_enhance", Build.VERSION_CODES.O);
|
||||
moduleMinApiFallbacks.put("riru_edxposed", Build.VERSION_CODES.O);
|
||||
moduleMinApiFallbacks.put("zygisk_edxposed", Build.VERSION_CODES.O);
|
||||
moduleMinApiFallbacks.put("riru_lsposed", Build.VERSION_CODES.O_MR1);
|
||||
moduleMinApiFallbacks.put("zygisk_lsposed", Build.VERSION_CODES.O_MR1);
|
||||
moduleMinApiFallbacks.put("noneDisplayCutout", Build.VERSION_CODES.P);
|
||||
moduleMinApiFallbacks.put("quickstepswitcher", Build.VERSION_CODES.P);
|
||||
moduleMinApiFallbacks.put("riru_clipboard_whitelist", Build.VERSION_CODES.Q);
|
||||
// minApi for riru core include submodules
|
||||
moduleMinApiFallbacks.put("riru-core", RIRU_MIN_API = Build.VERSION_CODES.M);
|
||||
// Fallbacks in case updateJson is missing
|
||||
final String GH_UC = "https://raw.githubusercontent.com/";
|
||||
moduleUpdateJsonFallbacks.put("BluetoothLibraryPatcher",
|
||||
GH_UC + "3arthur6/BluetoothLibraryPatcher/master/update.json");
|
||||
moduleUpdateJsonFallbacks.put("Detach",
|
||||
GH_UC + "xerta555/Detach-Files/blob/master/Updater.json");
|
||||
for (String module : new String[]{"busybox-ndk", "adb-ndk", "twrp-keep",
|
||||
"adreno-dev", "nano-ndk", "zipsigner", "nexusmedia", "mtd-ndk"}) {
|
||||
moduleUpdateJsonFallbacks.put(module,
|
||||
GH_UC + "Magisk-Modules-Repo/" + module + "/master/update.json");
|
||||
// Note: These fallback values may not be up-to-date
|
||||
// They are only used if modules don't define the metadata
|
||||
init {
|
||||
// Support are pages or groups where the user can get support for the module
|
||||
moduleSupportsFallbacks["aospill"] = "https://t.me/PannekoX"
|
||||
moduleSupportsFallbacks["bromitewebview"] = "https://t.me/androidacy_discussions"
|
||||
moduleSupportsFallbacks["fontrevival"] = "https://t.me/androidacy_discussions"
|
||||
moduleSupportsFallbacks["MagiskHidePropsConf"] = "https://forum.xda-developers.com/t" +
|
||||
"/module-magiskhide-props-config-safetynet-prop-edits-and-more-v6-1-1.3789228/"
|
||||
moduleSupportsFallbacks["quickstepswitcher"] = "https://t.me/QuickstepSwitcherSupport"
|
||||
moduleSupportsFallbacks["riru_edxposed"] = "https://t.me/EdXposed"
|
||||
moduleSupportsFallbacks["riru_lsposed"] = "https://github.com/LSPosed/LSPosed/issues"
|
||||
moduleSupportsFallbacks["substratum"] =
|
||||
"https://github.com/substratum/substratum/issues"
|
||||
// Config are application installed by modules that allow them to be configured
|
||||
moduleConfigsFallbacks["quickstepswitcher"] = "xyz.paphonb.quickstepswitcher"
|
||||
moduleConfigsFallbacks["hex_installer_module"] = "project.vivid.hex.bodhi"
|
||||
moduleConfigsFallbacks["riru_edxposed"] = "org.meowcat.edxposed.manager"
|
||||
moduleConfigsFallbacks["riru_lsposed"] = "org.lsposed.manager"
|
||||
moduleConfigsFallbacks["zygisk_lsposed"] = "org.lsposed.manager"
|
||||
moduleConfigsFallbacks["xposed_dalvik"] = "de.robv.android.xposed.installer"
|
||||
moduleConfigsFallbacks["xposed"] = "de.robv.android.xposed.installer"
|
||||
moduleConfigsFallbacks["substratum"] = "projekt.substratum"
|
||||
// minApi is the minimum android version required to use the module
|
||||
moduleMinApiFallbacks["HideNavBar"] = Build.VERSION_CODES.Q
|
||||
moduleMinApiFallbacks["riru_ifw_enhance"] = Build.VERSION_CODES.O
|
||||
moduleMinApiFallbacks["zygisk_ifw_enhance"] = Build.VERSION_CODES.O
|
||||
moduleMinApiFallbacks["riru_edxposed"] = Build.VERSION_CODES.O
|
||||
moduleMinApiFallbacks["zygisk_edxposed"] = Build.VERSION_CODES.O
|
||||
moduleMinApiFallbacks["riru_lsposed"] = Build.VERSION_CODES.O_MR1
|
||||
moduleMinApiFallbacks["zygisk_lsposed"] = Build.VERSION_CODES.O_MR1
|
||||
moduleMinApiFallbacks["noneDisplayCutout"] = Build.VERSION_CODES.P
|
||||
moduleMinApiFallbacks["quickstepswitcher"] = Build.VERSION_CODES.P
|
||||
moduleMinApiFallbacks["riru_clipboard_whitelist"] = Build.VERSION_CODES.Q
|
||||
// minApi for riru core include submodules
|
||||
moduleMinApiFallbacks["riru-core"] = Build.VERSION_CODES.M.also { RIRU_MIN_API = it }
|
||||
// Fallbacks in case updateJson is missing
|
||||
val ghUC = "https://raw.githubusercontent.com/"
|
||||
moduleUpdateJsonFallbacks["BluetoothLibraryPatcher"] =
|
||||
ghUC + "3arthur6/BluetoothLibraryPatcher/master/update.json"
|
||||
moduleUpdateJsonFallbacks["Detach"] =
|
||||
ghUC + "xerta555/Detach-Files/blob/master/Updater.json"
|
||||
for (module in arrayOf(
|
||||
"busybox-ndk", "adb-ndk", "twrp-keep",
|
||||
"adreno-dev", "nano-ndk", "zipsigner", "nexusmedia", "mtd-ndk"
|
||||
)) {
|
||||
moduleUpdateJsonFallbacks[module] =
|
||||
ghUC + "Magisk-Modules-Repo/" + module + "/master/update.json"
|
||||
}
|
||||
moduleUpdateJsonFallbacks["riru_ifw_enhance"] = "https://github.com/" +
|
||||
"Kr328/Riru-IFWEnhance/releases/latest/download/riru-ifw-enhance.json"
|
||||
moduleUpdateJsonFallbacks["zygisk_ifw_enhance"] = "https://github.com/" +
|
||||
"Kr328/Riru-IFWEnhance/releases/latest/download/zygisk-ifw-enhance.json"
|
||||
moduleUpdateJsonFallbacks["riru_lsposed"] =
|
||||
"https://lsposed.github.io/LSPosed/release/riru.json"
|
||||
moduleUpdateJsonFallbacks["zygisk_lsposed"] =
|
||||
"https://lsposed.github.io/LSPosed/release/zygisk.json"
|
||||
}
|
||||
moduleUpdateJsonFallbacks.put("riru_ifw_enhance", "https://github.com/" +
|
||||
"Kr328/Riru-IFWEnhance/releases/latest/download/riru-ifw-enhance.json");
|
||||
moduleUpdateJsonFallbacks.put("zygisk_ifw_enhance", "https://github.com/" +
|
||||
"Kr328/Riru-IFWEnhance/releases/latest/download/zygisk-ifw-enhance.json");
|
||||
moduleUpdateJsonFallbacks.put("riru_lsposed",
|
||||
"https://lsposed.github.io/LSPosed/release/riru.json");
|
||||
moduleUpdateJsonFallbacks.put("zygisk_lsposed",
|
||||
"https://lsposed.github.io/LSPosed/release/zygisk.json");
|
||||
}
|
||||
|
||||
public static void readProperties(ModuleInfo moduleInfo, String file,
|
||||
boolean local) throws IOException {
|
||||
readProperties(moduleInfo, SuFileInputStream.open(file), file, local);
|
||||
}
|
||||
@Throws(IOException::class)
|
||||
fun readProperties(
|
||||
moduleInfo: ModuleInfo, file: String,
|
||||
local: Boolean
|
||||
) {
|
||||
readProperties(moduleInfo, SuFileInputStream.open(file), file, local)
|
||||
}
|
||||
|
||||
public static void readProperties(ModuleInfo moduleInfo, String file,
|
||||
String name, boolean local) throws IOException {
|
||||
readProperties(moduleInfo, SuFileInputStream.open(file), name, local);
|
||||
}
|
||||
@Throws(IOException::class)
|
||||
fun readProperties(
|
||||
moduleInfo: ModuleInfo, file: String?,
|
||||
name: String, local: Boolean
|
||||
) {
|
||||
readProperties(moduleInfo, SuFileInputStream.open(file!!), name, local)
|
||||
}
|
||||
|
||||
public static void readProperties(ModuleInfo moduleInfo, InputStream inputStream,
|
||||
String name, boolean local) throws IOException {
|
||||
boolean readId = false, readIdSec = false, readName = false,
|
||||
readVersionCode = false, readVersion = false, readDescription = false,
|
||||
readUpdateJson = false, invalid = false, readMinApi = false, readMaxApi = false,
|
||||
readMMTReborn = false;
|
||||
try (BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
int lineNum = 0;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (lineNum == 0 && line.startsWith("\u0000")) {
|
||||
while (line.startsWith("\u0000"))
|
||||
line = line.substring(1);
|
||||
}
|
||||
lineNum++;
|
||||
int index = line.indexOf('=');
|
||||
if (index == -1 || line.startsWith("#"))
|
||||
continue;
|
||||
String key = line.substring(0, index);
|
||||
String value = line.substring(index + 1).trim();
|
||||
// check if field is defined on the moduleInfo object we are reading
|
||||
if (moduleInfo.toString().contains(key)) {
|
||||
continue;
|
||||
}
|
||||
// name and id have their own implementation
|
||||
if (isInvalidValue(key)) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
continue;
|
||||
} else throw new IOException("Invalid key at line " + lineNum);
|
||||
} else {
|
||||
if (value.isEmpty() && !moduleImportantProp.contains(key))
|
||||
continue; // allow empty values to pass.
|
||||
if (isInvalidValue(value)) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
continue;
|
||||
} else throw new IOException("Invalid value for key " + key);
|
||||
@Throws(IOException::class)
|
||||
fun readProperties(
|
||||
moduleInfo: ModuleInfo, inputStream: InputStream?,
|
||||
name: String, local: Boolean
|
||||
) {
|
||||
var readId = false
|
||||
var readIdSec = false
|
||||
var readName = false
|
||||
var readVersionCode = false
|
||||
var readVersion = false
|
||||
var readDescription = false
|
||||
var readUpdateJson = false
|
||||
var invalid = false
|
||||
var readMinApi = false
|
||||
var readMaxApi = false
|
||||
var readMMTReborn = false
|
||||
BufferedReader(
|
||||
InputStreamReader(inputStream, StandardCharsets.UTF_8)
|
||||
).use { bufferedReader ->
|
||||
var line: String
|
||||
var lineNum = 0
|
||||
while (bufferedReader.readLine().also { line = it } != null) {
|
||||
if (lineNum == 0 && line.startsWith("\u0000")) {
|
||||
while (line.startsWith("\u0000")) line = line.substring(1)
|
||||
}
|
||||
}
|
||||
switch (key) {
|
||||
case "id":
|
||||
lineNum++
|
||||
val index = line.indexOf('=')
|
||||
if (index == -1 || line.startsWith("#")) continue
|
||||
val key = line.substring(0, index)
|
||||
val value = line.substring(index + 1).trim { it <= ' ' }
|
||||
// check if field is defined on the moduleInfo object we are reading
|
||||
if (moduleInfo.toString().contains(key)) {
|
||||
continue
|
||||
}
|
||||
// name and id have their own implementation
|
||||
if (isInvalidValue(key)) {
|
||||
if (local) {
|
||||
invalid = true
|
||||
continue
|
||||
} else throw IOException("Invalid key at line $lineNum")
|
||||
} else {
|
||||
if (value.isEmpty() && !moduleImportantProp.contains(key)) continue // allow empty values to pass.
|
||||
if (isInvalidValue(value)) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
break;
|
||||
invalid = true
|
||||
continue
|
||||
} else throw IOException("Invalid value for key $key")
|
||||
}
|
||||
}
|
||||
when (key) {
|
||||
"id" -> {
|
||||
if (isInvalidValue(value)) {
|
||||
if (local) {
|
||||
invalid = true
|
||||
break
|
||||
}
|
||||
throw IOException("Invalid module id!")
|
||||
}
|
||||
readId = true
|
||||
if (moduleInfo.id != value) {
|
||||
invalid = if (local) {
|
||||
true
|
||||
} else {
|
||||
throw IOException(
|
||||
name + " has an non matching module id! " +
|
||||
"(Expected \"" + moduleInfo.id + "\" got \"" + value + "\""
|
||||
)
|
||||
}
|
||||
}
|
||||
throw new IOException("Invalid module id!");
|
||||
}
|
||||
readId = true;
|
||||
if (!moduleInfo.id.equals(value)) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
} else {
|
||||
throw new IOException(name + " has an non matching module id! " +
|
||||
"(Expected \"" + moduleInfo.id + "\" got \"" + value + "\"");
|
||||
|
||||
"name" -> {
|
||||
if (readName) {
|
||||
if (local) {
|
||||
invalid = true
|
||||
break
|
||||
} else throw IOException("Duplicate module name!")
|
||||
}
|
||||
if (isInvalidValue(value)) {
|
||||
if (local) {
|
||||
invalid = true
|
||||
break
|
||||
}
|
||||
throw IOException("Invalid module name!")
|
||||
}
|
||||
readName = true
|
||||
moduleInfo.name = value
|
||||
if (moduleInfo.id == value) {
|
||||
readIdSec = true
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "name":
|
||||
if (readName) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
break;
|
||||
} else throw new IOException("Duplicate module name!");
|
||||
|
||||
"version" -> {
|
||||
readVersion = true
|
||||
moduleInfo.version = value
|
||||
}
|
||||
if (isInvalidValue(value)) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
break;
|
||||
|
||||
"versionCode" -> {
|
||||
readVersionCode = true
|
||||
try {
|
||||
moduleInfo.versionCode = value.toLong()
|
||||
} catch (e: RuntimeException) {
|
||||
if (local) {
|
||||
invalid = true
|
||||
moduleInfo.versionCode = 0
|
||||
} else throw e
|
||||
}
|
||||
throw new IOException("Invalid module name!");
|
||||
}
|
||||
readName = true;
|
||||
moduleInfo.name = value;
|
||||
if (moduleInfo.id.equals(value)) {
|
||||
readIdSec = true;
|
||||
|
||||
"author" -> moduleInfo.author =
|
||||
if (value.endsWith(" development team")) value.substring(
|
||||
0,
|
||||
value.length - 17
|
||||
) else value
|
||||
|
||||
"description" -> {
|
||||
moduleInfo.description = value
|
||||
readDescription = true
|
||||
}
|
||||
|
||||
"updateJsonAk3" -> {
|
||||
// Only allow AnyKernel3 helper to use "updateJsonAk3"
|
||||
if ("ak3-helper" != moduleInfo.id) break
|
||||
if (isInvalidURL(value)) break
|
||||
moduleInfo.updateJson = value
|
||||
readUpdateJson = true
|
||||
}
|
||||
break;
|
||||
case "version":
|
||||
readVersion = true;
|
||||
moduleInfo.version = value;
|
||||
break;
|
||||
case "versionCode":
|
||||
readVersionCode = true;
|
||||
try {
|
||||
moduleInfo.versionCode = Long.parseLong(value);
|
||||
} catch (RuntimeException e) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
moduleInfo.versionCode = 0;
|
||||
} else throw e;
|
||||
|
||||
"updateJson" -> {
|
||||
if (isInvalidURL(value)) break
|
||||
moduleInfo.updateJson = value
|
||||
readUpdateJson = true
|
||||
}
|
||||
|
||||
"changeBoot" -> moduleInfo.changeBoot =
|
||||
java.lang.Boolean.parseBoolean(value)
|
||||
|
||||
"mmtReborn" -> {
|
||||
moduleInfo.mmtReborn = java.lang.Boolean.parseBoolean(value)
|
||||
readMMTReborn = true
|
||||
}
|
||||
|
||||
"support" -> {
|
||||
// Do not accept invalid or too broad support links
|
||||
if (isInvalidURL(value) || "https://forum.xda-developers.com/" == value) break
|
||||
moduleInfo.support = value
|
||||
}
|
||||
break;
|
||||
case "author":
|
||||
moduleInfo.author = value.endsWith(" development team") ?
|
||||
value.substring(0, value.length() - 17) : value;
|
||||
break;
|
||||
case "description":
|
||||
moduleInfo.description = value;
|
||||
readDescription = true;
|
||||
break;
|
||||
case "updateJsonAk3":
|
||||
// Only allow AnyKernel3 helper to use "updateJsonAk3"
|
||||
if (!"ak3-helper".equals(moduleInfo.id)) break;
|
||||
case "updateJson":
|
||||
if (isInvalidURL(value)) break;
|
||||
moduleInfo.updateJson = value;
|
||||
readUpdateJson = true;
|
||||
break;
|
||||
case "changeBoot":
|
||||
moduleInfo.changeBoot = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "mmtReborn":
|
||||
moduleInfo.mmtReborn = Boolean.parseBoolean(value);
|
||||
readMMTReborn = true;
|
||||
break;
|
||||
case "support":
|
||||
// Do not accept invalid or too broad support links
|
||||
if (isInvalidURL(value) ||
|
||||
"https://forum.xda-developers.com/".equals(value))
|
||||
break;
|
||||
moduleInfo.support = value;
|
||||
break;
|
||||
case "donate":
|
||||
// Do not accept invalid donate links
|
||||
if (isInvalidURL(value)) break;
|
||||
moduleInfo.donate = value;
|
||||
break;
|
||||
case "config":
|
||||
moduleInfo.config = value;
|
||||
break;
|
||||
case "needRamdisk":
|
||||
moduleInfo.needRamdisk = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "minMagisk":
|
||||
try {
|
||||
int i = value.indexOf('.');
|
||||
|
||||
"donate" -> {
|
||||
// Do not accept invalid donate links
|
||||
if (isInvalidURL(value)) break
|
||||
moduleInfo.donate = value
|
||||
}
|
||||
|
||||
"config" -> moduleInfo.config = value
|
||||
"needRamdisk" -> moduleInfo.needRamdisk =
|
||||
java.lang.Boolean.parseBoolean(value)
|
||||
|
||||
"minMagisk" -> try {
|
||||
val i = value.indexOf('.')
|
||||
if (i == -1) {
|
||||
moduleInfo.minMagisk = Integer.parseInt(value);
|
||||
moduleInfo.minMagisk = value.toInt()
|
||||
} else {
|
||||
moduleInfo.minMagisk = // Allow 24.1 to mean 24100
|
||||
(Integer.parseInt(value.substring(0, i)) * 1000) +
|
||||
(Integer.parseInt(value.substring(i + 1)) * 100);
|
||||
moduleInfo.minMagisk = // Allow 24.1 to mean 24100
|
||||
value.substring(0, i).toInt() * 1000 + value.substring(i + 1)
|
||||
.toInt() * 100
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
moduleInfo.minMagisk = 0
|
||||
}
|
||||
|
||||
"minApi" -> {
|
||||
// Special case for Riru EdXposed because
|
||||
// minApi don't mean the same thing for them
|
||||
if ("10" == value) break
|
||||
try {
|
||||
moduleInfo.minApi = value.toInt()
|
||||
readMinApi = true
|
||||
} catch (e: Exception) {
|
||||
if (!readMinApi) moduleInfo.minApi = 0
|
||||
}
|
||||
} catch (Exception e) {
|
||||
moduleInfo.minMagisk = 0;
|
||||
}
|
||||
break;
|
||||
case "minApi":
|
||||
// Special case for Riru EdXposed because
|
||||
// minApi don't mean the same thing for them
|
||||
if ("10".equals(value)) break;
|
||||
case "minSdkVersion": // Improve compatibility
|
||||
try {
|
||||
moduleInfo.minApi = Integer.parseInt(value);
|
||||
readMinApi = true;
|
||||
} catch (Exception e) {
|
||||
if (!readMinApi) moduleInfo.minApi = 0;
|
||||
|
||||
"minSdkVersion" -> try {
|
||||
moduleInfo.minApi = value.toInt()
|
||||
readMinApi = true
|
||||
} catch (e: Exception) {
|
||||
if (!readMinApi) moduleInfo.minApi = 0
|
||||
}
|
||||
break;
|
||||
case "maxSdkVersion": // Improve compatibility
|
||||
case "maxApi":
|
||||
try {
|
||||
moduleInfo.maxApi = Integer.parseInt(value);
|
||||
readMaxApi = true;
|
||||
} catch (Exception e) {
|
||||
if (!readMaxApi) moduleInfo.maxApi = 0;
|
||||
|
||||
"maxSdkVersion", "maxApi" -> try {
|
||||
moduleInfo.maxApi = value.toInt()
|
||||
readMaxApi = true
|
||||
} catch (e: Exception) {
|
||||
if (!readMaxApi) moduleInfo.maxApi = 0
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!readId) {
|
||||
if (readIdSec && local) {
|
||||
// Using the name for module id is not really appropriate, so beautify it a bit
|
||||
moduleInfo.name = makeNameFromId(moduleInfo.id);
|
||||
} else if (!local) { // Allow local modules to not declare ids
|
||||
throw new IOException("Didn't read module id at least once!");
|
||||
if (!readId) {
|
||||
if (readIdSec && local) {
|
||||
// Using the name for module id is not really appropriate, so beautify it a bit
|
||||
moduleInfo.name = makeNameFromId(moduleInfo.id)
|
||||
} else if (!local) { // Allow local modules to not declare ids
|
||||
throw IOException("Didn't read module id at least once!")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!readVersionCode) {
|
||||
if (local) {
|
||||
invalid = true;
|
||||
moduleInfo.versionCode = 0;
|
||||
if (!readVersionCode) {
|
||||
if (local) {
|
||||
invalid = true
|
||||
moduleInfo.versionCode = 0
|
||||
} else {
|
||||
throw IOException("Didn't read module versionCode at least once!")
|
||||
}
|
||||
}
|
||||
if (!readName || isInvalidValue(moduleInfo.name)) {
|
||||
moduleInfo.name = makeNameFromId(moduleInfo.id)
|
||||
}
|
||||
if (!readVersion) {
|
||||
moduleInfo.version = "v" + moduleInfo.versionCode
|
||||
} else {
|
||||
throw new IOException("Didn't read module versionCode at least once!");
|
||||
moduleInfo.version = shortenVersionName(
|
||||
moduleInfo.version, moduleInfo.versionCode
|
||||
)
|
||||
}
|
||||
if (!readDescription || isInvalidValue(moduleInfo.description)) {
|
||||
moduleInfo.description = ""
|
||||
}
|
||||
if (!readUpdateJson) {
|
||||
moduleInfo.updateJson = moduleUpdateJsonFallbacks[moduleInfo.id]
|
||||
}
|
||||
if (moduleInfo.minApi == 0 || !readMinApi) {
|
||||
val minApiFallback = moduleMinApiFallbacks[moduleInfo.id]
|
||||
if (minApiFallback != null) moduleInfo.minApi =
|
||||
minApiFallback else if (moduleInfo.id.startsWith("riru_")
|
||||
|| moduleInfo.id.startsWith("riru-")
|
||||
) moduleInfo.minApi = RIRU_MIN_API
|
||||
}
|
||||
if (moduleInfo.support == null) {
|
||||
moduleInfo.support = moduleSupportsFallbacks[moduleInfo.id]
|
||||
}
|
||||
if (moduleInfo.config == null) {
|
||||
moduleInfo.config = moduleConfigsFallbacks[moduleInfo.id]
|
||||
}
|
||||
if (!readMMTReborn) {
|
||||
moduleInfo.mmtReborn = moduleMTTRebornFallback.contains(moduleInfo.id) ||
|
||||
AppUpdateManager.getFlagsForModule(moduleInfo.id) and
|
||||
AppUpdateManager.FLAG_COMPAT_MMT_REBORN != 0
|
||||
}
|
||||
// All local modules should have an author
|
||||
// set to "Unknown" if author is missing.
|
||||
if (local && moduleInfo.author == null) {
|
||||
moduleInfo.author = "Unknown"
|
||||
}
|
||||
if (invalid) {
|
||||
moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_METADATA_INVALID
|
||||
// This shouldn't happen but just in case
|
||||
if (!local) throw IOException("Invalid properties!")
|
||||
}
|
||||
}
|
||||
if (!readName || isInvalidValue(moduleInfo.name)) {
|
||||
moduleInfo.name = makeNameFromId(moduleInfo.id);
|
||||
}
|
||||
if (!readVersion) {
|
||||
moduleInfo.version = "v" + moduleInfo.versionCode;
|
||||
} else {
|
||||
moduleInfo.version = shortenVersionName(
|
||||
moduleInfo.version, moduleInfo.versionCode);
|
||||
}
|
||||
if (!readDescription || isInvalidValue(moduleInfo.description)) {
|
||||
moduleInfo.description = "";
|
||||
}
|
||||
if (!readUpdateJson) {
|
||||
moduleInfo.updateJson = moduleUpdateJsonFallbacks.get(moduleInfo.id);
|
||||
}
|
||||
if (moduleInfo.minApi == 0 || !readMinApi) {
|
||||
Integer minApiFallback = moduleMinApiFallbacks.get(moduleInfo.id);
|
||||
if (minApiFallback != null)
|
||||
moduleInfo.minApi = minApiFallback;
|
||||
else if (moduleInfo.id.startsWith("riru_")
|
||||
|| moduleInfo.id.startsWith("riru-"))
|
||||
moduleInfo.minApi = RIRU_MIN_API;
|
||||
}
|
||||
if (moduleInfo.support == null) {
|
||||
moduleInfo.support = moduleSupportsFallbacks.get(moduleInfo.id);
|
||||
}
|
||||
if (moduleInfo.config == null) {
|
||||
moduleInfo.config = moduleConfigsFallbacks.get(moduleInfo.id);
|
||||
}
|
||||
if (!readMMTReborn) {
|
||||
moduleInfo.mmtReborn = moduleMTTRebornFallback.contains(moduleInfo.id) ||
|
||||
(AppUpdateManager.getFlagsForModule(moduleInfo.id) &
|
||||
AppUpdateManager.FLAG_COMPAT_MMT_REBORN) != 0;
|
||||
}
|
||||
// All local modules should have an author
|
||||
// set to "Unknown" if author is missing.
|
||||
if (local && moduleInfo.author == null) {
|
||||
moduleInfo.author = "Unknown";
|
||||
}
|
||||
if (invalid) {
|
||||
moduleInfo.flags |= ModuleInfo.FLAG_METADATA_INVALID;
|
||||
// This shouldn't happen but just in case
|
||||
if (!local) throw new IOException("Invalid properties!");
|
||||
}
|
||||
}
|
||||
|
||||
public static String readModulePropSimple(InputStream inputStream, String what) {
|
||||
if (inputStream == null) return null;
|
||||
String moduleId = null;
|
||||
try (BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
while (line.startsWith("\u0000"))
|
||||
line = line.substring(1);
|
||||
if (line.startsWith(what + "=")) {
|
||||
moduleId = line.substring(what.length() + 1).trim();
|
||||
@JvmStatic
|
||||
fun readModulePropSimple(inputStream: InputStream?, what: String): String? {
|
||||
if (inputStream == null) return null
|
||||
var moduleId: String? = null
|
||||
try {
|
||||
BufferedReader(
|
||||
InputStreamReader(inputStream, StandardCharsets.UTF_8)
|
||||
).use { bufferedReader ->
|
||||
var line: String
|
||||
while (bufferedReader.readLine().also { line = it } != null) {
|
||||
while (line.startsWith("\u0000")) line = line.substring(1)
|
||||
if (line.startsWith("$what=")) {
|
||||
moduleId = line.substring(what.length + 1).trim { it <= ' ' }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.i(e)
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Timber.i(e);
|
||||
return moduleId
|
||||
}
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
public static String readModuleId(InputStream inputStream) {
|
||||
return readModulePropSimple(inputStream, "id");
|
||||
}
|
||||
|
||||
public static void applyFallbacks(ModuleInfo moduleInfo) {
|
||||
if (moduleInfo.support == null || moduleInfo.support.isEmpty()) {
|
||||
moduleInfo.support = moduleSupportsFallbacks.get(moduleInfo.id);
|
||||
}
|
||||
if (moduleInfo.config == null || moduleInfo.config.isEmpty()) {
|
||||
moduleInfo.config = moduleConfigsFallbacks.get(moduleInfo.id);
|
||||
fun readModuleId(inputStream: InputStream?): String? {
|
||||
return readModulePropSimple(inputStream, "id")
|
||||
}
|
||||
if (moduleInfo.minApi == 0) {
|
||||
Integer minApiFallback = moduleMinApiFallbacks.get(moduleInfo.id);
|
||||
if (minApiFallback != null)
|
||||
moduleInfo.minApi = minApiFallback;
|
||||
else if (moduleInfo.id.startsWith("riru_")
|
||||
|| moduleInfo.id.startsWith("riru-"))
|
||||
moduleInfo.minApi = RIRU_MIN_API;
|
||||
|
||||
@JvmStatic
|
||||
fun applyFallbacks(moduleInfo: ModuleInfo) {
|
||||
if (moduleInfo.support == null || moduleInfo.support!!.isEmpty()) {
|
||||
moduleInfo.support = moduleSupportsFallbacks[moduleInfo.id]
|
||||
}
|
||||
if (moduleInfo.config == null || moduleInfo.config!!.isEmpty()) {
|
||||
moduleInfo.config = moduleConfigsFallbacks[moduleInfo.id]
|
||||
}
|
||||
if (moduleInfo.minApi == 0) {
|
||||
val minApiFallback = moduleMinApiFallbacks[moduleInfo.id]
|
||||
if (minApiFallback != null) moduleInfo.minApi =
|
||||
minApiFallback else if (moduleInfo.id.startsWith("riru_")
|
||||
|| moduleInfo.id.startsWith("riru-")
|
||||
) moduleInfo.minApi = RIRU_MIN_API
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some module are really so low quality that it has become very annoying.
|
||||
public static boolean isLowQualityModule(ModuleInfo moduleInfo) {
|
||||
final String description;
|
||||
return moduleInfo == null || moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)
|
||||
|| moduleInfo.name.length() < 3 || moduleInfo.versionCode < 0
|
||||
|| moduleInfo.author == null || !TextUtils.isGraphic(moduleInfo.author)
|
||||
|| isNullString(description = moduleInfo.description) || !TextUtils.isGraphic(description)
|
||||
|| description.toLowerCase(Locale.ROOT).equals(moduleInfo.name.toLowerCase(Locale.ROOT))
|
||||
|| (getFlagsForModule(moduleInfo.id) & FLAG_COMPAT_LOW_QUALITY) != 0
|
||||
|| (moduleInfo.id.startsWith("."));
|
||||
}
|
||||
// Some module are really so low quality that it has become very annoying.
|
||||
@JvmStatic
|
||||
fun isLowQualityModule(moduleInfo: ModuleInfo?): Boolean {
|
||||
var description: String = moduleInfo?.description ?: return true
|
||||
return (moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID) || moduleInfo.name!!.length < 3 || moduleInfo.versionCode < 0 || moduleInfo.author == null || !TextUtils.isGraphic(
|
||||
moduleInfo.author
|
||||
) || isNullString(moduleInfo.description.also {
|
||||
description = it!!
|
||||
}) || !TextUtils.isGraphic(description)) || description.lowercase() == moduleInfo.name!!.lowercase() || AppUpdateManager.getFlagsForModule(
|
||||
moduleInfo.id
|
||||
) and AppUpdateManager.FLAG_COMPAT_LOW_QUALITY != 0 || moduleInfo.id.startsWith(".")
|
||||
}
|
||||
|
||||
private static boolean isInvalidValue(String name) {
|
||||
return !TextUtils.isGraphic(name) || name.indexOf('\0') != -1;
|
||||
}
|
||||
private fun isInvalidValue(name: String?): Boolean {
|
||||
return !TextUtils.isGraphic(name) || name!!.indexOf('\u0000') != -1
|
||||
}
|
||||
|
||||
public static boolean isInvalidURL(String url) {
|
||||
int i = url.indexOf('/', 8);
|
||||
int e = url.indexOf('.', 8);
|
||||
return i == -1 || e == -1 || e >= i || !url.startsWith("https://")
|
||||
|| url.length() <= 12 || url.indexOf('\0') != -1;
|
||||
}
|
||||
@JvmStatic
|
||||
fun isInvalidURL(url: String): Boolean {
|
||||
val i = url.indexOf('/', 8)
|
||||
val e = url.indexOf('.', 8)
|
||||
return i == -1 || e == -1 || e >= i || !url.startsWith("https://") || url.length <= 12 || url.indexOf(
|
||||
'\u0000'
|
||||
) != -1
|
||||
}
|
||||
|
||||
public static String makeNameFromId(String moduleId) {
|
||||
return moduleId.substring(0, 1).toUpperCase(Locale.ROOT) +
|
||||
moduleId.substring(1).replace('_', ' ');
|
||||
}
|
||||
private fun makeNameFromId(moduleId: String): String {
|
||||
return moduleId.substring(0, 1).uppercase() +
|
||||
moduleId.substring(1).replace('_', ' ')
|
||||
}
|
||||
|
||||
public static boolean isNullString(String string) {
|
||||
return string == null || string.isEmpty() || "null".equals(string);
|
||||
}
|
||||
@JvmStatic
|
||||
fun isNullString(string: String?): Boolean {
|
||||
return string.isNullOrEmpty() || "null" == string
|
||||
}
|
||||
|
||||
// Make versionName no longer than 16 charters to avoid UI overflow.
|
||||
public static String shortenVersionName(String versionName, long versionCode) {
|
||||
if (isNullString(versionName)) return "v" + versionCode;
|
||||
if (versionName.length() <= 16) return versionName;
|
||||
int i = versionName.lastIndexOf('.');
|
||||
if (i != -1 && i <= 16 && versionName.indexOf('.') != i
|
||||
&& versionName.indexOf(' ') == -1)
|
||||
return versionName.substring(0, i);
|
||||
return "v" + versionCode;
|
||||
// Make versionName no longer than 16 charters to avoid UI overflow.
|
||||
fun shortenVersionName(versionName: String?, versionCode: Long): String {
|
||||
if (isNullString(versionName)) return "v$versionCode"
|
||||
if (versionName!!.length <= 16) return versionName
|
||||
val i = versionName.lastIndexOf('.')
|
||||
return if (i != -1 && i <= 16 && versionName.indexOf('.') != i && versionName.indexOf(
|
||||
' '
|
||||
) == -1
|
||||
) versionName.substring(0, i) else "v$versionCode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue