1- package com.fox2code.mmm ;
1+ package com.fox2code.mmm
22
3- import com.fox2code.mmm.utils.io.Files;
4- import com.fox2code.mmm.utils.io.net.Http;
5-
6- import org.json.JSONObject;
7-
8- import java.io.BufferedReader;
9- import java.io.File;
10- import java.io.FileInputStream;
11- import java.io.IOException;
12- import java.io.InputStream;
13- import java.io.InputStreamReader;
14- import java.nio.charset.StandardCharsets;
15- import java.util.HashMap;
16-
17- import timber.log.Timber;
3+ import com.fox2code.mmm.utils.io.Files.Companion.write
4+ import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
5+ import org.json.JSONObject
6+ import timber.log.Timber
7+ import java.io.BufferedReader
8+ import java.io.File
9+ import java.io.FileInputStream
10+ import java.io.IOException
11+ import java.io.InputStream
12+ import java.io.InputStreamReader
13+ import java.nio.charset.StandardCharsets
1814
1915// See https://docs.github.com/en/rest/reference/repos#releases
20- public class AppUpdateManager {
21- public static final int FLAG_COMPAT_LOW_QUALITY = 0x0001 ;
22- public static final int FLAG_COMPAT_NO_EXT = 0x0002 ;
23- public static final int FLAG_COMPAT_MAGISK_CMD = 0x0004 ;
24- public static final int FLAG_COMPAT_NEED_32BIT = 0x0008 ;
25- public static final int FLAG_COMPAT_MALWARE = 0x0010 ;
26- public static final int FLAG_COMPAT_NO_ANSI = 0x0020 ;
27- public static final int FLAG_COMPAT_FORCE_ANSI = 0x0040 ;
28- public static final int FLAG_COMPAT_FORCE_HIDE = 0x0080 ;
29- public static final int FLAG_COMPAT_MMT_REBORN = 0x0100 ;
30- public static final int FLAG_COMPAT_ZIP_WRAPPER = 0x0200 ;
31- public static final String RELEASES_API_URL = " https://api.github.com/repos/Androidacy/MagiskModuleManager/releases/latest" ;
32- private static final AppUpdateManager INSTANCE = new AppUpdateManager ();
33- private final HashMap <String , Integer > compatDataId = new HashMap <> ();
34- private final Object updateLock = new Object ();
35- private final File compatFile;
36- private String latestRelease;
37- private long lastChecked;
16+ @Suppress(" unused" )
17+ class AppUpdateManager private constructor() {
18+ private val compatDataId = HashMap <String , Int >()
19+ private val updateLock = Any ()
20+ private val compatFile: File = File (MainApplication .getINSTANCE().filesDir, " compat.txt" )
21+ private var latestRelease: String?
22+ private var lastChecked: Long
3823
39- private AppUpdateManager () {
40- this .compatFile = new File ( MainApplication .getINSTANCE().getFilesDir(), " compat.txt " );
41- this .latestRelease = MainApplication .getBootSharedPreferences(). getString(" updater_latest_release" , BuildConfig .VERSION_NAME );
42- this . lastChecked = 0 ;
43- if (this . compatFile.isFile() ) {
24+ init {
25+ latestRelease = MainApplication .getBootSharedPreferences()
26+ . getString(" updater_latest_release" , BuildConfig .VERSION_NAME )
27+ lastChecked = 0
28+ if (compatFile.isFile) {
4429 try {
45- this .parseCompatibilityFlags(new FileInputStream (this .compatFile));
46- } catch (
47- IOException ignored) {
30+ parseCompatibilityFlags(FileInputStream (compatFile))
31+ } catch (ignored: IOException ) {
4832 }
4933 }
5034 }
5135
52- public static AppUpdateManager getAppUpdateManager() {
53- return INSTANCE ;
54- }
55-
56- public static int getFlagsForModule(String moduleId) {
57- return INSTANCE .getCompatibilityFlags(moduleId);
58- }
59-
60- public static boolean shouldForceHide(String repoId) {
61- if (BuildConfig .DEBUG || repoId.startsWith(" repo_" ) || repoId.equals(" magisk_alt_repo" ))
62- return false ;
63- return ! repoId.startsWith(" repo_" ) && (INSTANCE .getCompatibilityFlags(repoId) & FLAG_COMPAT_FORCE_HIDE ) != 0 ;
64- }
65-
6636 // Return true if should show a notification
67- public boolean checkUpdate(boolean force) {
68- if (! BuildConfig .ENABLE_AUTO_UPDATER )
69- return false ;
70- if (! force && this .peekShouldUpdate())
71- return true ;
72- long lastChecked = this .lastChecked;
73- if (lastChecked != 0 &&
74- // Avoid spam calls by putting a 60 seconds timer
75- lastChecked < System .currentTimeMillis() - 60000L )
76- return force && this .peekShouldUpdate();
77- synchronized (this .updateLock) {
78- if (lastChecked != this .lastChecked)
79- return this .peekShouldUpdate();
37+ fun checkUpdate (force : Boolean ): Boolean {
38+ if (! BuildConfig .ENABLE_AUTO_UPDATER ) return false
39+ if (! force && peekShouldUpdate()) return true
40+ val lastChecked = lastChecked
41+ if (lastChecked != 0L && // Avoid spam calls by putting a 60 seconds timer
42+ lastChecked < System .currentTimeMillis() - 60000L
43+ ) return force && peekShouldUpdate()
44+ synchronized(updateLock) {
45+ if (lastChecked != this .lastChecked) return peekShouldUpdate()
8046 try {
81- JSONObject release = new JSONObject (new String (Http .doHttpGet(RELEASES_API_URL , false ), StandardCharsets .UTF_8 ));
82- String latestRelease = null ;
83- boolean preRelease = false ;
47+ val release =
48+ JSONObject (String (doHttpGet(RELEASES_API_URL , false ), StandardCharsets .UTF_8 ))
49+ var latestRelease: String? = null
50+ var preRelease = false
8451 // get latest_release from tag_name translated to int
8552 if (release.has(" tag_name" )) {
86- latestRelease = release.getString(" tag_name" );
87- preRelease = release.getBoolean(" prerelease" );
53+ latestRelease = release.getString(" tag_name" )
54+ preRelease = release.getBoolean(" prerelease" )
8855 }
89- Timber .d(" Latest release: %s, isPreRelease: %s" , latestRelease, preRelease);
90- if (latestRelease == null )
91- return false ;
56+ Timber .d(" Latest release: %s, isPreRelease: %s" , latestRelease, preRelease)
57+ if (latestRelease == null ) return false
9258 if (preRelease) {
93- this .latestRelease = " 99999999" ; // prevent updating to pre-release
94- return false ;
59+ this .latestRelease = " 99999999" // prevent updating to pre-release
60+ return false
9561 }
96- this .latestRelease = latestRelease;
97- this .lastChecked = System .currentTimeMillis();
98- } catch (
99- Exception ioe) {
100- Timber .e(ioe);
62+ this .latestRelease = latestRelease
63+ this .lastChecked = System .currentTimeMillis()
64+ } catch (ioe: Exception ) {
65+ Timber .e(ioe)
10166 }
10267 }
103- return this . peekShouldUpdate();
68+ return peekShouldUpdate()
10469 }
10570
106- public void checkUpdateCompat() {
107- compatDataId.clear();
71+ fun checkUpdateCompat () {
72+ compatDataId.clear()
10873 try {
109- Files .write(compatFile, new byte[0 ]);
110- } catch (
111- IOException e) {
112- Timber .e(e);
74+ write(compatFile, ByteArray (0 ))
75+ } catch (e: IOException ) {
76+ Timber .e(e)
11377 }
11478 // There once lived an implementation that used a GitHub API to get the compatibility flags. It was removed because it was too slow and the API was rate limited.
115- Timber .w(" Remote compatibility data flags are not implemented." );
79+ Timber .w(" Remote compatibility data flags are not implemented." )
11680 }
11781
118- public boolean peekShouldUpdate() {
119- if (! BuildConfig .ENABLE_AUTO_UPDATER || BuildConfig .DEBUG )
120- return false ;
82+ fun peekShouldUpdate (): Boolean {
83+ if (! BuildConfig .ENABLE_AUTO_UPDATER || BuildConfig .DEBUG ) return false
12184 // Convert both BuildConfig.VERSION_NAME and latestRelease to int
122- int currentVersion = 0 , latestVersion = 0 ;
85+ var currentVersion = 0
86+ var latestVersion = 0
12387 try {
124- currentVersion = Integer .parseInt(BuildConfig .VERSION_NAME .replaceAll(" \\ D" , " " ));
125- latestVersion = Integer .parseInt(this .latestRelease.replace(" v" , " " ).replaceAll(" \\ D" , " " ));
126- } catch (
127- NumberFormatException ignored) {
88+ currentVersion = BuildConfig .VERSION_NAME .replace(" \\ D" .toRegex(), " " ).toInt()
89+ latestVersion = latestRelease!! .replace(" v" , " " ).replace(" \\ D" .toRegex(), " " ).toInt()
90+ } catch (ignored: NumberFormatException ) {
12891 }
129- return currentVersion < latestVersion;
92+ return currentVersion < latestVersion
13093 }
13194
132- public boolean peekHasUpdate() {
133- if (! BuildConfig .ENABLE_AUTO_UPDATER || BuildConfig .DEBUG )
134- return false ;
135- return this .peekShouldUpdate();
95+ fun peekHasUpdate (): Boolean {
96+ return if (! BuildConfig .ENABLE_AUTO_UPDATER || BuildConfig .DEBUG ) false else peekShouldUpdate()
13697 }
13798
138- private void parseCompatibilityFlags(InputStream inputStream) throws IOException {
139- compatDataId.clear();
140- BufferedReader bufferedReader = new BufferedReader (new InputStreamReader (inputStream, StandardCharsets .UTF_8 ));
141- String line;
142- while ((line = bufferedReader.readLine()) != null ) {
143- line = line.trim();
144- if (line.isEmpty() || line.startsWith(" #" ))
145- continue ;
146- int i = line.indexOf(' /' );
147- if (i == - 1 )
148- continue ;
149- int value = 0 ;
150- for (String arg : line.substring(i + 1 ).split(" ," )) {
151- switch (arg) {
152- default -> {
99+ @Throws(IOException ::class )
100+ private fun parseCompatibilityFlags (inputStream : InputStream ) {
101+ compatDataId.clear()
102+ val bufferedReader = BufferedReader (InputStreamReader (inputStream, StandardCharsets .UTF_8 ))
103+ var line: String
104+ while (bufferedReader.readLine().also { line = it } != null ) {
105+ line = line.trim { it <= ' ' }
106+ if (line.isEmpty() || line.startsWith(" #" )) continue
107+ val i = line.indexOf(' /' )
108+ if (i == - 1 ) continue
109+ var value = 0
110+ for (arg in line.substring(i + 1 ).split(" ," .toRegex()).dropLastWhile { it.isEmpty() }
111+ .toTypedArray()) {
112+ when (arg) {
113+ " lowQuality" -> value = value or FLAG_COMPAT_LOW_QUALITY
114+ " noExt" -> value = value or FLAG_COMPAT_NO_EXT
115+ " magiskCmd" -> value = value or FLAG_COMPAT_MAGISK_CMD
116+ " need32bit" -> value = value or FLAG_COMPAT_NEED_32BIT
117+ " malware" -> value = value or FLAG_COMPAT_MALWARE
118+ " noANSI" -> value = value or FLAG_COMPAT_NO_ANSI
119+ " forceANSI" -> value = value or FLAG_COMPAT_FORCE_ANSI
120+ " forceHide" -> value = value or FLAG_COMPAT_FORCE_HIDE
121+ " mmtReborn" -> value = value or FLAG_COMPAT_MMT_REBORN
122+ " wrapper" -> value = value or FLAG_COMPAT_ZIP_WRAPPER
123+ else -> {
124+ run {}
125+ value = value or FLAG_COMPAT_LOW_QUALITY
126+ value = value or FLAG_COMPAT_NO_EXT
127+ value = value or FLAG_COMPAT_MAGISK_CMD
128+ value = value or FLAG_COMPAT_NEED_32BIT
129+ value = value or FLAG_COMPAT_MALWARE
130+ value = value or FLAG_COMPAT_NO_ANSI
131+ value = value or FLAG_COMPAT_FORCE_ANSI
132+ value = value or FLAG_COMPAT_FORCE_HIDE
133+ value = value or FLAG_COMPAT_MMT_REBORN
134+ value = value or FLAG_COMPAT_ZIP_WRAPPER
153135 }
154- case " lowQuality" -> value |= FLAG_COMPAT_LOW_QUALITY ;
155- case " noExt" -> value |= FLAG_COMPAT_NO_EXT ;
156- case " magiskCmd" -> value |= FLAG_COMPAT_MAGISK_CMD ;
157- case " need32bit" -> value |= FLAG_COMPAT_NEED_32BIT ;
158- case " malware" -> value |= FLAG_COMPAT_MALWARE ;
159- case " noANSI" -> value |= FLAG_COMPAT_NO_ANSI ;
160- case " forceANSI" -> value |= FLAG_COMPAT_FORCE_ANSI ;
161- case " forceHide" -> value |= FLAG_COMPAT_FORCE_HIDE ;
162- case " mmtReborn" -> value |= FLAG_COMPAT_MMT_REBORN ;
163- case " wrapper" -> value |= FLAG_COMPAT_ZIP_WRAPPER ;
164136 }
165137 }
166- compatDataId.put( line.substring(0 , i), value);
138+ compatDataId[ line.substring(0 , i)] = value
167139 }
168- bufferedReader.close();
140+ bufferedReader.close()
141+ }
142+
143+ fun getCompatibilityFlags (moduleId : String ): Int {
144+ val compatFlags = compatDataId[moduleId]
145+ return compatFlags ? : 0
169146 }
170147
171- public int getCompatibilityFlags(String moduleId) {
172- Integer compatFlags = compatDataId.get(moduleId);
173- return compatFlags == null ? 0 : compatFlags;
148+ companion object {
149+ const val FLAG_COMPAT_LOW_QUALITY = 0x0001
150+ const val FLAG_COMPAT_NO_EXT = 0x0002
151+ const val FLAG_COMPAT_MAGISK_CMD = 0x0004
152+ const val FLAG_COMPAT_NEED_32BIT = 0x0008
153+ const val FLAG_COMPAT_MALWARE = 0x0010
154+ const val FLAG_COMPAT_NO_ANSI = 0x0020
155+ const val FLAG_COMPAT_FORCE_ANSI = 0x0040
156+ const val FLAG_COMPAT_FORCE_HIDE = 0x0080
157+ const val FLAG_COMPAT_MMT_REBORN = 0x0100
158+ const val FLAG_COMPAT_ZIP_WRAPPER = 0x0200
159+ const val RELEASES_API_URL =
160+ " https://api.github.com/repos/Androidacy/MagiskModuleManager/releases/latest"
161+ val appUpdateManager = AppUpdateManager ()
162+ fun getFlagsForModule (moduleId : String ): Int {
163+ return appUpdateManager.getCompatibilityFlags(moduleId)
164+ }
165+
166+ @JvmStatic
167+ fun shouldForceHide (repoId : String ): Boolean {
168+ return if (BuildConfig .DEBUG || repoId.startsWith(" repo_" ) || repoId == " magisk_alt_repo" ) false else ! repoId.startsWith(
169+ " repo_"
170+ ) && appUpdateManager.getCompatibilityFlags(repoId) and FLAG_COMPAT_FORCE_HIDE != 0
171+ }
174172 }
175- }
173+ }
0 commit comments