Show quality notifiers in modules cards, and fix Magisk repo workaround.

pull/27/head
Fox2Code 4 years ago
parent b9bb6d4a2e
commit b97eaf140a

@ -5,7 +5,7 @@ public class Constants {
public static final int MAGISK_VER_CODE_UTIL_INSTALL = 20400;
public static final int MAGISK_VER_CODE_PATH_SUPPORT = 21000;
public static final int MAGISK_VER_CODE_INSTALL_COMMAND = 21200;
public static final int MAGISK_VER_CODE_MAGISK_ZYGOTE = 23002;
public static final int MAGISK_VER_CODE_MAGISK_ZYGOTE = 24000;
public static final String INTENT_INSTALL_INTERNAL =
BuildConfig.APPLICATION_ID + ".intent.action.INSTALL_MODULE_INTERNAL";
public static final String INTENT_ANDROIDACY_INTERNAL =

@ -21,6 +21,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.fox2code.mmm.manager.LocalModuleInfo;
import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.manager.ModuleManager;
import com.fox2code.mmm.repo.RepoModule;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.topjohnwu.superuser.internal.UiThreadHandler;
@ -199,10 +200,14 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter<ModuleViewAdap
}
String updateText = moduleHolder.getUpdateTimeText();
if (!updateText.isEmpty()) {
RepoModule repoModule = moduleHolder.repoModule;
this.updateText.setVisibility(View.VISIBLE);
this.updateText.setText(
this.getString(R.string.module_last_update) + " " + updateText + "\n" +
this.getString(R.string.module_repo) + " " + moduleHolder.getRepoName());
this.getString(R.string.module_repo) + " " + moduleHolder.getRepoName() +
(repoModule.qualityText == 0 ? "" : (
"\n" + this.getString(repoModule.qualityText) +
" " + repoModule.qualityValue)));
} else if (moduleHolder.moduleId.equals("hosts")) {
this.updateText.setVisibility(View.VISIBLE);
this.updateText.setText(R.string.magisk_builtin_module);

@ -28,6 +28,12 @@ public class AndroidacyRepoData extends RepoData {
public AndroidacyRepoData(String url, File cacheRoot,
SharedPreferences cachedPreferences) {
super(url, cacheRoot, cachedPreferences);
if (this.metaDataCache.exists()) {
this.androidacyBlockade = this.metaDataCache.lastModified() + 5_000L;
if (this.androidacyBlockade - 10_000L > System.currentTimeMillis()) {
this.androidacyBlockade = 0; // Don't allow time travel
}
}
}
@Override
@ -185,6 +191,13 @@ public class AndroidacyRepoData extends RepoData {
@Override
public boolean tryLoadMetadata(RepoModule repoModule) {
return true;
if (this.moduleHashMap.containsKey(repoModule.id)) {
repoModule.moduleInfo.flags &=
~ModuleInfo.FLAG_METADATA_INVALID;
return true;
}
repoModule.moduleInfo.flags |=
ModuleInfo.FLAG_METADATA_INVALID;
return false;
}
}

@ -4,6 +4,9 @@ import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import com.fox2code.mmm.R;
import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Http;
@ -27,6 +30,7 @@ import java.util.Map;
import java.util.Objects;
public class RepoData {
private static final String TAG = "RepoData";
private final Object populateLock = new Object();
public final String url;
public final File cacheRoot;
@ -36,7 +40,7 @@ public class RepoData {
public final HashMap<String, RepoModule> moduleHashMap;
public long lastUpdate;
public String name;
private final Map<String, Long> specialTimes;
private final Map<String, SpecialData> specialData;
private long specialLastUpdate;
protected RepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) {
@ -51,7 +55,7 @@ public class RepoData {
this.special = special;
this.moduleHashMap = new HashMap<>();
this.name = this.url; // Set url as default name
this.specialTimes = special ? new HashMap<>() : Collections.emptyMap();
this.specialData = special ? new HashMap<>() : Collections.emptyMap();
if (!this.cacheRoot.isDirectory()) {
this.cacheRoot.mkdirs();
} else {
@ -63,10 +67,13 @@ public class RepoData {
Files.read(this.metaDataCache), StandardCharsets.UTF_8));
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
this.specialTimes.put(jsonObject.getString("name"),
Objects.requireNonNull(ISO_OFFSET_DATE_TIME.parse(
jsonObject.getString("pushed_at"))).getTime());
Log.d("RepoData", "Got " +
this.specialData.put(
jsonObject.getString("name"), new SpecialData(
Objects.requireNonNull(ISO_OFFSET_DATE_TIME.parse(
jsonObject.getString(
"pushed_at"))).getTime(),
jsonObject.optInt("stargazers_count")));
Log.d(TAG, "Got " +
jsonObject.getString("name") + " from local storage!");
}
this.specialLastUpdate = metaDataCacheSpecial.lastModified();
@ -79,6 +86,10 @@ public class RepoData {
}
}
if (this.metaDataCache.exists()) {
this.lastUpdate = metaDataCache.lastModified();
if (this.lastUpdate > System.currentTimeMillis()) {
this.lastUpdate = 0; // Don't allow time travel
}
try {
List<RepoModule> modules = this.populate(new JSONObject(
new String(Files.read(this.metaDataCache), StandardCharsets.UTF_8)));
@ -108,7 +119,6 @@ public class RepoData {
for (RepoModule repoModule : this.moduleHashMap.values()) {
repoModule.processed = false;
}
Log.d("RepoData", "Data: " + this.specialTimes.toString());
JSONArray array = jsonObject.getJSONArray("modules");
int len = array.length();
for (int i = 0; i < len; i++) {
@ -116,16 +126,15 @@ public class RepoData {
String moduleId = module.getString("id");
// Deny remote modules ids shorter than 3 chars long or that start with a digit
if (moduleId.length() < 3 || Character.isDigit(moduleId.charAt(0))) continue;
Long moduleLastUpdateSpecial = this.specialTimes.get(moduleId);
SpecialData moduleSpecialData = this.specialData.get(moduleId);
long moduleLastUpdate = module.getLong("last_update");
String moduleNotesUrl = module.getString("notes_url");
String modulePropsUrl = module.getString("prop_url");
String moduleZipUrl = module.getString("zip_url");
String moduleChecksum = module.optString("checksum");
if (moduleLastUpdateSpecial != null) { // Fix last update time
Log.d("RepoData", "Data: " + moduleLastUpdate + " -> " +
moduleLastUpdateSpecial + " for " + moduleId);
moduleLastUpdate = Math.max(moduleLastUpdate, moduleLastUpdateSpecial);
String moduleStars = module.optString("stars");
if (moduleSpecialData != null) { // Fix last update time
moduleLastUpdate = Math.max(moduleLastUpdate, moduleSpecialData.time);
moduleNotesUrl = Http.updateLink(moduleNotesUrl);
modulePropsUrl = Http.updateLink(modulePropsUrl);
moduleZipUrl = Http.updateLink(moduleZipUrl);
@ -152,6 +161,15 @@ public class RepoData {
repoModule.propUrl = modulePropsUrl;
repoModule.zipUrl = moduleZipUrl;
repoModule.checksum = moduleChecksum;
if (moduleSpecialData != null) {
repoModule.qualityValue = moduleSpecialData.stars;
repoModule.qualityText = R.string.module_stars;
} else if (!moduleStars.isEmpty()) {
try {
repoModule.qualityValue = Integer.parseInt(moduleStars);
repoModule.qualityText = R.string.module_stars;
} catch (NumberFormatException ignored) {}
}
}
// Remove no longer existing modules
Iterator<RepoModule> moduleInfoIterator = this.moduleHashMap.values().iterator();
@ -184,6 +202,12 @@ public class RepoData {
if (moduleInfo.version == null) {
moduleInfo.version = "v" + moduleInfo.versionCode;
}
SpecialData moduleSpecialData =
this.specialData.get(repoModule.id);
if (moduleSpecialData != null) {
repoModule.qualityValue = moduleSpecialData.stars;
repoModule.qualityText = R.string.module_stars;
}
return true;
} catch (Exception ignored) {
file.delete();
@ -200,20 +224,24 @@ public class RepoData {
public void updateSpecialTimes(boolean force) throws IOException, JSONException {
if (!this.special) return;
synchronized (this.populateLock) {
if (this.specialLastUpdate == 0L ||
(force && this.specialLastUpdate < System.currentTimeMillis() - 60000L)) {
if (this.specialLastUpdate == 0L || (force && (this.specialData.isEmpty() ||
this.specialLastUpdate < System.currentTimeMillis() - 60000L))) {
File metaDataCacheSpecial = new File(cacheRoot, "modules_times.json");
this.specialTimes.clear();
this.specialData.clear();
try {
// Requesting only 32 most recently pushed repos
byte[] data = Http.doHttpGet(
"https://api.github.com/users/Magisk-Modules-Repo/repos",
false);
"https://api.github.com/users/Magisk-Modules-Repo/" +
"repos?sort=pushed&per_page=32", false);
JSONArray jsonArray = new JSONArray(new String(data, StandardCharsets.UTF_8));
for (int i = 0;i < jsonArray.length();i++) {
JSONObject jsonObject = jsonArray.optJSONObject(i);
this.specialTimes.put(jsonObject.getString("name"),
Objects.requireNonNull(ISO_OFFSET_DATE_TIME.parse(
jsonObject.getString("pushed_at"))).getTime());
this.specialData.put(
jsonObject.getString("name"), new SpecialData(
Objects.requireNonNull(ISO_OFFSET_DATE_TIME.parse(
jsonObject.getString(
"pushed_at"))).getTime(),
jsonObject.optInt("stargazers_count")));
}
Files.write(metaDataCacheSpecial, data);
this.specialLastUpdate = System.currentTimeMillis();
@ -229,4 +257,22 @@ public class RepoData {
this.name.equals(this.url) ?
fallback : this.name;
}
private static class SpecialData {
SpecialData(long time, int stars) {
this.time = time; this.stars = stars;
}
final long time;
final int stars;
@NonNull
@Override
public String toString() {
return "SpecialData{" +
"time=" + time +
", stars=" + stars +
'}';
}
}
}

@ -182,7 +182,8 @@ public final class RepoManager {
Log.d(TAG, "Registering " + repoData.name);
for (RepoModule repoModule:repoModules) {
try {
if (repoModule.propUrl != null) {
if (repoModule.propUrl != null &&
!repoModule.propUrl.isEmpty()) {
repoData.storeMetadata(repoModule,
Http.doHttpGet(repoModule.propUrl, false));
Files.write(new File(repoData.cacheRoot, repoModule.id + ".prop"),

@ -76,105 +76,117 @@
android:layout_below="@+id/credit_text"
app:layout_constraintTop_toBottomOf="@id/credit_text" />
<TextView
android:id="@+id/updated_text"
android:textSize="12sp"
android:text="@string/loading"
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:layout_margin="0dp"
android:background="@null"
app:layout_constraintTop_toBottomOf="@+id/description_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="@+id/updated_text"
android:textSize="12sp"
android:text="@string/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="bottom"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<!-- Module actions -->
<!-- Module actions -->
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action1"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintRight_toRightOf="parent"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginRight="3dp"
android:layout_marginBottom="4dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action1"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginRight="3dp"
android:layout_marginBottom="1dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action2"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintRight_toLeftOf="@id/button_action1"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action2"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_action1"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginBottom="1dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action3"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintRight_toLeftOf="@id/button_action2"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action3"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_action2"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginBottom="1dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action4"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintRight_toLeftOf="@id/button_action3"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action5"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintRight_toLeftOf="@id/button_action4"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action4"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_action3"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginBottom="1dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action6"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintRight_toLeftOf="@id/button_action5"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action5"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_action4"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginBottom="1dp"
tools:ignore="RtlHardcoded" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/button_action6"
android:textSize="16sp"
android:visibility="gone"
android:src="@drawable/ic_baseline_error_24"
android:layout_width="@dimen/module_action_icon_size"
android:layout_height="@dimen/module_action_icon_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_action5"
android:importantForAccessibility="no"
android:layout_marginLeft="8dp"
android:layout_marginBottom="1dp"
tools:ignore="RtlHardcoded" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

Loading…
Cancel
Save