Remove module repo completely
This commit is contained in:
parent
9fa92741ab
commit
ffc7980ac6
|
|
@ -34,12 +34,6 @@
|
|||
android:name=".ui.activity.EdDownloadActivity"
|
||||
android:label="@string/Install" />
|
||||
<activity android:name=".ui.activity.BlackListActivity" />
|
||||
<activity
|
||||
android:name=".ui.activity.DownloadDetailsActivity"
|
||||
android:label="@string/nav_item_download" />
|
||||
<activity
|
||||
android:name=".ui.activity.DownloadActivity"
|
||||
android:label="@string/Downloads" />
|
||||
<activity
|
||||
android:name=".ui.activity.ModuleScopeActivity"
|
||||
android:label="@string/menu_scope" />
|
||||
|
|
|
|||
|
|
@ -120,8 +120,6 @@ public class App extends Application implements Application.ActivityLifecycleCal
|
|||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
//RepoLoader.getInstance().triggerFirstLoadIfNecessary();
|
||||
}
|
||||
|
||||
private void registerReceivers() {
|
||||
|
|
@ -142,19 +140,6 @@ public class App extends Application implements Application.ActivityLifecycleCal
|
|||
mkdir("log");
|
||||
}
|
||||
|
||||
/*
|
||||
public void updateProgressIndicator(final SwipeRefreshLayout refreshLayout) {
|
||||
final boolean isLoading = RepoLoader.getInstance().isLoading() || ModuleUtil.getInstance().isLoading();
|
||||
runOnUiThread(() -> {
|
||||
synchronized (App.this) {
|
||||
if (currentActivity != null) {
|
||||
if (refreshLayout != null)
|
||||
refreshLayout.setRefreshing(isLoading);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public synchronized void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {
|
||||
if (isUiLoaded) {
|
||||
|
|
@ -182,13 +167,12 @@ public class App extends Application implements Application.ActivityLifecycleCal
|
|||
|
||||
@Override
|
||||
public synchronized void onActivityResumed(@NonNull Activity activity) {
|
||||
//currentActivity = (AppCompatActivity) activity;
|
||||
//updateProgressIndicator(null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityPaused(@NonNull Activity activity) {
|
||||
//currentActivity = null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.content.SharedPreferences;
|
|||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
|
|
@ -32,7 +33,6 @@ import com.bumptech.glide.request.transition.Transition;
|
|||
import org.meowcat.edxposed.manager.App;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.util.GlideApp;
|
||||
import org.meowcat.edxposed.manager.util.InstallApkUtil;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
|
@ -277,7 +277,7 @@ public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> impl
|
|||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ApplicationInfo info = showList.get(position);
|
||||
holder.appName.setText(InstallApkUtil.getAppLabel(info, pm));
|
||||
holder.appName.setText(getAppLabel(info, pm));
|
||||
try {
|
||||
PackageInfo packageInfo = pm.getPackageInfo(info.packageName, 0);
|
||||
GlideApp.with(holder.appIcon)
|
||||
|
|
@ -387,7 +387,7 @@ public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> impl
|
|||
ArrayList<ApplicationInfo> filtered = new ArrayList<>();
|
||||
String filter = constraint.toString().toLowerCase();
|
||||
for (ApplicationInfo info : fullList) {
|
||||
if (lowercaseContains(InstallApkUtil.getAppLabel(info, pm), filter)
|
||||
if (lowercaseContains(getAppLabel(info, pm), filter)
|
||||
|| lowercaseContains(info.packageName, filter)) {
|
||||
filtered.add(info);
|
||||
}
|
||||
|
|
@ -402,4 +402,15 @@ public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> impl
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
|
||||
try {
|
||||
if (info.labelRes > 0) {
|
||||
Resources res = pm.getResourcesForApplication(info);
|
||||
return res.getString(info.labelRes);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return info.loadLabel(pm).toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.adapters;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.DataSetObserver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
|
||||
|
||||
private Cursor cursor;
|
||||
|
||||
private boolean dataValid;
|
||||
|
||||
private int rowIdColumn;
|
||||
|
||||
private final DataSetObserver dataSetObserver;
|
||||
|
||||
public CursorRecyclerViewAdapter(Cursor cursor) {
|
||||
this.cursor = cursor;
|
||||
dataValid = cursor != null;
|
||||
rowIdColumn = dataValid ? cursor.getColumnIndex("_id") : -1;
|
||||
dataSetObserver = new NotifyingDataSetObserver();
|
||||
if (this.cursor != null) {
|
||||
this.cursor.registerDataSetObserver(dataSetObserver);
|
||||
}
|
||||
}
|
||||
|
||||
protected Cursor getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (dataValid && cursor != null) {
|
||||
return cursor.getCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
if (dataValid && cursor != null && cursor.moveToPosition(position)) {
|
||||
return cursor.getLong(rowIdColumn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHasStableIds(boolean hasStableIds) {
|
||||
super.setHasStableIds(true);
|
||||
}
|
||||
|
||||
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH viewHolder, int position) {
|
||||
if (!dataValid) {
|
||||
throw new IllegalStateException("this should only be called when the cursor is valid");
|
||||
}
|
||||
if (!cursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("couldn't move cursor to position " + position);
|
||||
}
|
||||
onBindViewHolder(viewHolder, cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
|
||||
* closed.
|
||||
*/
|
||||
public void changeCursor(Cursor cursor) {
|
||||
Cursor old = swapCursor(cursor);
|
||||
if (old != null) {
|
||||
old.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap in a new Cursor, returning the old Cursor. Unlike
|
||||
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
|
||||
* closed.
|
||||
*/
|
||||
private Cursor swapCursor(Cursor newCursor) {
|
||||
if (newCursor == cursor) {
|
||||
return null;
|
||||
}
|
||||
final Cursor oldCursor = cursor;
|
||||
if (oldCursor != null && dataSetObserver != null) {
|
||||
oldCursor.unregisterDataSetObserver(dataSetObserver);
|
||||
}
|
||||
cursor = newCursor;
|
||||
if (cursor != null) {
|
||||
if (dataSetObserver != null) {
|
||||
cursor.registerDataSetObserver(dataSetObserver);
|
||||
}
|
||||
rowIdColumn = newCursor.getColumnIndexOrThrow("_id");
|
||||
dataValid = true;
|
||||
} else {
|
||||
rowIdColumn = -1;
|
||||
dataValid = false;
|
||||
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
return oldCursor;
|
||||
}
|
||||
|
||||
private class NotifyingDataSetObserver extends DataSetObserver {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
dataValid = true;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
super.onInvalidated();
|
||||
dataValid = false;
|
||||
notifyDataSetChanged();
|
||||
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.receivers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.meowcat.edxposed.manager.BuildConfig;
|
||||
import org.meowcat.edxposed.manager.util.NotificationUtil;
|
||||
import org.meowcat.edxposed.manager.util.TaskRunner;
|
||||
import org.meowcat.edxposed.manager.util.json.JSONUtils;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
//new TaskRunner().executeAsync(new LongRunningTask());
|
||||
}
|
||||
|
||||
private static class LongRunningTask implements Callable<Void> {
|
||||
|
||||
@Override
|
||||
public Void call() {
|
||||
try {
|
||||
Thread.sleep(60 * 60 * 1000);
|
||||
String jsonString = JSONUtils.getFileContent(JSONUtils.JSON_LINK).replace("%XPOSED_ZIP%", "");
|
||||
|
||||
String newApkVersion = new JSONObject(jsonString).getJSONObject("apk").getString("version");
|
||||
|
||||
Integer a = BuildConfig.VERSION_CODE;
|
||||
Integer b = Integer.valueOf(newApkVersion);
|
||||
|
||||
if (a.compareTo(b) < 0) {
|
||||
NotificationUtil.showInstallerUpdateNotification();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class Module {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public final Repository repository;
|
||||
public final List<Pair<String, String>> moreInfo = new LinkedList<>();
|
||||
public final List<ModuleVersion> versions = new ArrayList<>();
|
||||
final List<String> screenshots = new ArrayList<>();
|
||||
public String packageName;
|
||||
public String name;
|
||||
public String summary;
|
||||
public String description;
|
||||
public boolean descriptionIsHtml = false;
|
||||
public String author;
|
||||
public String support;
|
||||
long created = -1;
|
||||
long updated = -1;
|
||||
|
||||
Module(Repository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
public class ModuleVersion {
|
||||
public final Module module;
|
||||
public String name;
|
||||
public int code;
|
||||
public String downloadLink;
|
||||
public String md5sum;
|
||||
public String changelog;
|
||||
public boolean changelogIsHtml = false;
|
||||
public ReleaseType relType = ReleaseType.STABLE;
|
||||
public long uploaded = -1;
|
||||
|
||||
/* package */ ModuleVersion(Module module) {
|
||||
this.module = module;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
|
||||
public enum ReleaseType {
|
||||
STABLE(R.string.reltype_stable, R.string.reltype_stable_summary), BETA(R.string.reltype_beta, R.string.reltype_beta_summary), EXPERIMENTAL(R.string.reltype_experimental, R.string.reltype_experimental_summary);
|
||||
|
||||
private static final ReleaseType[] sValuesCache = values();
|
||||
private final int mTitleId;
|
||||
private final int mSummaryId;
|
||||
|
||||
ReleaseType(int titleId, int summaryId) {
|
||||
mTitleId = titleId;
|
||||
mSummaryId = summaryId;
|
||||
}
|
||||
|
||||
public static ReleaseType fromString(String value) {
|
||||
if (value == null || value.equals("stable"))
|
||||
return STABLE;
|
||||
else if (value.equals("beta"))
|
||||
return BETA;
|
||||
else if (value.equals("experimental"))
|
||||
return EXPERIMENTAL;
|
||||
else
|
||||
return STABLE;
|
||||
}
|
||||
|
||||
public static ReleaseType fromOrdinal(int ordinal) {
|
||||
return sValuesCache[ordinal];
|
||||
}
|
||||
|
||||
public int getTitleId() {
|
||||
return mTitleId;
|
||||
}
|
||||
|
||||
public int getSummaryId() {
|
||||
return mSummaryId;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,492 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.meowcat.edxposed.manager.App;
|
||||
import org.meowcat.edxposed.manager.BuildConfig;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.InstalledModulesColumns;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.InstalledModulesUpdatesColumns;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.ModuleVersionsColumns;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.ModulesColumns;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.MoreInfoColumns;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.OverviewColumns;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.OverviewColumnsIndexes;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions.RepositoriesColumns;
|
||||
import org.meowcat.edxposed.manager.util.ModuleUtil;
|
||||
import org.meowcat.edxposed.manager.util.ModuleUtil.InstalledModule;
|
||||
import org.meowcat.edxposed.manager.util.RepoLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
public final class RepoDb extends SQLiteOpenHelper {
|
||||
public static final int SORT_STATUS = 0;
|
||||
public static final int SORT_UPDATED = 1;
|
||||
private static final int SORT_CREATED = 2;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Context context;
|
||||
private static final SQLiteDatabase db;
|
||||
|
||||
static {
|
||||
RepoDb instance = new RepoDb(App.getInstance());
|
||||
db = instance.getWritableDatabase();
|
||||
db.execSQL("PRAGMA foreign_keys=ON");
|
||||
instance.createTempTables(db);
|
||||
}
|
||||
|
||||
private RepoDb(Context context) {
|
||||
super(context, getDbPath(context), null, RepoDbDefinitions.DATABASE_VERSION);
|
||||
RepoDb.context = context;
|
||||
}
|
||||
|
||||
private static String getDbPath(Context context) {
|
||||
return new File(context.getNoBackupFilesDir(), RepoDbDefinitions.DATABASE_NAME).getPath();
|
||||
}
|
||||
|
||||
public static void beginTransation() {
|
||||
db.beginTransaction();
|
||||
}
|
||||
|
||||
public static void setTransactionSuccessful() {
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
|
||||
public static void endTransation() {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
private static String getString(@SuppressWarnings("SameParameterValue") String table, @SuppressWarnings("SameParameterValue") String searchColumn, String searchValue, @SuppressWarnings("SameParameterValue") String resultColumn) {
|
||||
String[] projection = new String[]{resultColumn};
|
||||
String where = searchColumn + " = ?";
|
||||
String[] whereArgs = new String[]{searchValue};
|
||||
Cursor c = db.query(table, projection, where, whereArgs, null, null, null, "1");
|
||||
if (c.moveToFirst()) {
|
||||
String result = c.getString(c.getColumnIndexOrThrow(resultColumn));
|
||||
c.close();
|
||||
return result;
|
||||
} else {
|
||||
c.close();
|
||||
throw new RowNotFoundException("Could not find " + table + "." + searchColumn + " with value '" + searchValue + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static long insertRepository(String url) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RepositoriesColumns.URL, url);
|
||||
return db.insertOrThrow(RepositoriesColumns.TABLE_NAME, null, values);
|
||||
}
|
||||
|
||||
public static void deleteRepositories() {
|
||||
if (db != null)
|
||||
db.delete(RepositoriesColumns.TABLE_NAME, null, null);
|
||||
}
|
||||
|
||||
public static Map<Long, Repository> getRepositories() {
|
||||
Map<Long, Repository> result = new LinkedHashMap<>(1);
|
||||
|
||||
String[] projection = new String[]{
|
||||
RepositoriesColumns._ID,
|
||||
RepositoriesColumns.URL,
|
||||
RepositoriesColumns.TITLE,
|
||||
RepositoriesColumns.PARTIAL_URL,
|
||||
RepositoriesColumns.VERSION,
|
||||
};
|
||||
|
||||
Cursor c = db.query(RepositoriesColumns.TABLE_NAME, projection, null, null, null, null, RepositoriesColumns._ID);
|
||||
while (c.moveToNext()) {
|
||||
Repository repo = new Repository();
|
||||
long id = c.getLong(c.getColumnIndexOrThrow(RepositoriesColumns._ID));
|
||||
repo.url = c.getString(c.getColumnIndexOrThrow(RepositoriesColumns.URL));
|
||||
repo.name = c.getString(c.getColumnIndexOrThrow(RepositoriesColumns.TITLE));
|
||||
repo.partialUrl = c.getString(c.getColumnIndexOrThrow(RepositoriesColumns.PARTIAL_URL));
|
||||
repo.version = c.getString(c.getColumnIndexOrThrow(RepositoriesColumns.VERSION));
|
||||
result.put(id, repo);
|
||||
}
|
||||
c.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void updateRepository(long repoId, Repository repository) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RepositoriesColumns.TITLE, repository.name);
|
||||
values.put(RepositoriesColumns.PARTIAL_URL, repository.partialUrl);
|
||||
values.put(RepositoriesColumns.VERSION, repository.version);
|
||||
db.update(RepositoriesColumns.TABLE_NAME, values, RepositoriesColumns._ID + " = ?", new String[]{Long.toString(repoId)});
|
||||
}
|
||||
|
||||
public static void updateRepositoryVersion(long repoId, String version) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RepositoriesColumns.VERSION, version);
|
||||
db.update(RepositoriesColumns.TABLE_NAME, values, RepositoriesColumns._ID + " = ?", new String[]{Long.toString(repoId)});
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static long insertModule(long repoId, Module mod) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ModulesColumns.REPO_ID, repoId);
|
||||
values.put(ModulesColumns.PKGNAME, mod.packageName);
|
||||
values.put(ModulesColumns.TITLE, mod.name);
|
||||
values.put(ModulesColumns.SUMMARY, mod.summary);
|
||||
values.put(ModulesColumns.DESCRIPTION, mod.description);
|
||||
values.put(ModulesColumns.DESCRIPTION_IS_HTML, mod.descriptionIsHtml);
|
||||
values.put(ModulesColumns.AUTHOR, mod.author);
|
||||
values.put(ModulesColumns.SUPPORT, mod.support);
|
||||
values.put(ModulesColumns.CREATED, mod.created);
|
||||
values.put(ModulesColumns.UPDATED, mod.updated);
|
||||
|
||||
ModuleVersion latestVersion = RepoLoader.getInstance().getLatestVersion(mod);
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
long moduleId = db.insertOrThrow(ModulesColumns.TABLE_NAME, null, values);
|
||||
|
||||
long latestVersionId = -1;
|
||||
for (ModuleVersion version : mod.versions) {
|
||||
long versionId = insertModuleVersion(moduleId, version);
|
||||
if (latestVersion == version)
|
||||
latestVersionId = versionId;
|
||||
}
|
||||
|
||||
if (latestVersionId > -1) {
|
||||
values = new ContentValues();
|
||||
values.put(ModulesColumns.LATEST_VERSION, latestVersionId);
|
||||
db.update(ModulesColumns.TABLE_NAME, values, ModulesColumns._ID + " = ?", new String[]{Long.toString(moduleId)});
|
||||
}
|
||||
|
||||
for (Pair<String, String> moreInfoEntry : mod.moreInfo) {
|
||||
insertMoreInfo(moduleId, moreInfoEntry.first, moreInfoEntry.second);
|
||||
}
|
||||
|
||||
// TODO Add mod.screenshots
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
return moduleId;
|
||||
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
private static long insertModuleVersion(long moduleId, ModuleVersion version) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ModuleVersionsColumns.MODULE_ID, moduleId);
|
||||
values.put(ModuleVersionsColumns.NAME, version.name);
|
||||
values.put(ModuleVersionsColumns.CODE, version.code);
|
||||
values.put(ModuleVersionsColumns.DOWNLOAD_LINK, version.downloadLink);
|
||||
values.put(ModuleVersionsColumns.MD5SUM, version.md5sum);
|
||||
values.put(ModuleVersionsColumns.CHANGELOG, version.changelog);
|
||||
values.put(ModuleVersionsColumns.CHANGELOG_IS_HTML, version.changelogIsHtml);
|
||||
values.put(ModuleVersionsColumns.RELTYPE, version.relType.ordinal());
|
||||
values.put(ModuleVersionsColumns.UPLOADED, version.uploaded);
|
||||
return db.insertOrThrow(ModuleVersionsColumns.TABLE_NAME, null,
|
||||
values);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
private static long insertMoreInfo(long moduleId, String title, String value) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MoreInfoColumns.MODULE_ID, moduleId);
|
||||
values.put(MoreInfoColumns.LABEL, title);
|
||||
values.put(MoreInfoColumns.VALUE, value);
|
||||
return db.insertOrThrow(MoreInfoColumns.TABLE_NAME, null, values);
|
||||
}
|
||||
|
||||
public static void deleteAllModules(long repoId) {
|
||||
db.delete(ModulesColumns.TABLE_NAME, ModulesColumns.REPO_ID + " = ?", new String[]{Long.toString(repoId)});
|
||||
}
|
||||
|
||||
public static void deleteModule(long repoId, String packageName) {
|
||||
db.delete(ModulesColumns.TABLE_NAME, ModulesColumns.REPO_ID + " = ? AND " + ModulesColumns.PKGNAME + " = ?", new String[]{Long.toString(repoId), packageName});
|
||||
}
|
||||
|
||||
public static Module getModuleByPackageName(String packageName) {
|
||||
// The module itself
|
||||
String[] projection = new String[]{
|
||||
ModulesColumns._ID,
|
||||
ModulesColumns.REPO_ID,
|
||||
ModulesColumns.PKGNAME,
|
||||
ModulesColumns.TITLE,
|
||||
ModulesColumns.SUMMARY,
|
||||
ModulesColumns.DESCRIPTION,
|
||||
ModulesColumns.DESCRIPTION_IS_HTML,
|
||||
ModulesColumns.AUTHOR,
|
||||
ModulesColumns.SUPPORT,
|
||||
ModulesColumns.CREATED,
|
||||
ModulesColumns.UPDATED,
|
||||
};
|
||||
|
||||
String where = ModulesColumns.PREFERRED + " = 1 AND " + ModulesColumns.PKGNAME + " = ?";
|
||||
String[] whereArgs = new String[]{packageName};
|
||||
|
||||
Cursor c = db.query(ModulesColumns.TABLE_NAME, projection, where, whereArgs, null, null, null, "1");
|
||||
if (!c.moveToFirst()) {
|
||||
c.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
long moduleId = c.getLong(c.getColumnIndexOrThrow(ModulesColumns._ID));
|
||||
long repoId = c.getLong(c.getColumnIndexOrThrow(ModulesColumns.REPO_ID));
|
||||
|
||||
Module mod = new Module(RepoLoader.getInstance().getRepository(repoId));
|
||||
mod.packageName = c.getString(c.getColumnIndexOrThrow(ModulesColumns.PKGNAME));
|
||||
mod.name = c.getString(c.getColumnIndexOrThrow(ModulesColumns.TITLE));
|
||||
mod.summary = c.getString(c.getColumnIndexOrThrow(ModulesColumns.SUMMARY));
|
||||
mod.description = c.getString(c.getColumnIndexOrThrow(ModulesColumns.DESCRIPTION));
|
||||
mod.descriptionIsHtml = c.getInt(c.getColumnIndexOrThrow(ModulesColumns.DESCRIPTION_IS_HTML)) > 0;
|
||||
mod.author = c.getString(c.getColumnIndexOrThrow(ModulesColumns.AUTHOR));
|
||||
mod.support = c.getString(c.getColumnIndexOrThrow(ModulesColumns.SUPPORT));
|
||||
mod.created = c.getLong(c.getColumnIndexOrThrow(ModulesColumns.CREATED));
|
||||
mod.updated = c.getLong(c.getColumnIndexOrThrow(ModulesColumns.UPDATED));
|
||||
|
||||
c.close();
|
||||
|
||||
// Versions
|
||||
projection = new String[]{
|
||||
ModuleVersionsColumns.NAME,
|
||||
ModuleVersionsColumns.CODE, ModuleVersionsColumns.DOWNLOAD_LINK,
|
||||
ModuleVersionsColumns.MD5SUM, ModuleVersionsColumns.CHANGELOG,
|
||||
ModuleVersionsColumns.CHANGELOG_IS_HTML,
|
||||
ModuleVersionsColumns.RELTYPE,
|
||||
ModuleVersionsColumns.UPLOADED,
|
||||
};
|
||||
|
||||
where = ModuleVersionsColumns.MODULE_ID + " = ?";
|
||||
whereArgs = new String[]{Long.toString(moduleId)};
|
||||
|
||||
c = db.query(ModuleVersionsColumns.TABLE_NAME, projection, where, whereArgs, null, null, null);
|
||||
while (c.moveToNext()) {
|
||||
ModuleVersion version = new ModuleVersion(mod);
|
||||
version.name = c.getString(c.getColumnIndexOrThrow(ModuleVersionsColumns.NAME));
|
||||
version.code = c.getInt(c.getColumnIndexOrThrow(ModuleVersionsColumns.CODE));
|
||||
version.downloadLink = c.getString(c.getColumnIndexOrThrow(ModuleVersionsColumns.DOWNLOAD_LINK));
|
||||
version.md5sum = c.getString(c.getColumnIndexOrThrow(ModuleVersionsColumns.MD5SUM));
|
||||
version.changelog = c.getString(c.getColumnIndexOrThrow(ModuleVersionsColumns.CHANGELOG));
|
||||
version.changelogIsHtml = c.getInt(c.getColumnIndexOrThrow(ModuleVersionsColumns.CHANGELOG_IS_HTML)) > 0;
|
||||
version.relType = ReleaseType.fromOrdinal(c.getInt(c.getColumnIndexOrThrow(ModuleVersionsColumns.RELTYPE)));
|
||||
version.uploaded = c.getLong(c.getColumnIndexOrThrow(ModuleVersionsColumns.UPLOADED));
|
||||
mod.versions.add(version);
|
||||
}
|
||||
c.close();
|
||||
|
||||
// MoreInfo
|
||||
projection = new String[]{
|
||||
MoreInfoColumns.LABEL,
|
||||
MoreInfoColumns.VALUE,
|
||||
};
|
||||
|
||||
where = MoreInfoColumns.MODULE_ID + " = ?";
|
||||
whereArgs = new String[]{Long.toString(moduleId)};
|
||||
|
||||
c = db.query(MoreInfoColumns.TABLE_NAME, projection, where, whereArgs, null, null, MoreInfoColumns._ID);
|
||||
while (c.moveToNext()) {
|
||||
String label = c.getString(c.getColumnIndexOrThrow(MoreInfoColumns.LABEL));
|
||||
String value = c.getString(c.getColumnIndexOrThrow(MoreInfoColumns.VALUE));
|
||||
mod.moreInfo.add(new Pair<>(label, value));
|
||||
}
|
||||
c.close();
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
public static String getModuleSupport(String packageName) {
|
||||
return getString(ModulesColumns.TABLE_NAME, ModulesColumns.PKGNAME, packageName, ModulesColumns.SUPPORT);
|
||||
}
|
||||
|
||||
public static void updateModuleLatestVersion(String packageName) {
|
||||
int maxShownReleaseType = RepoLoader.getInstance().getMaxShownReleaseType(packageName).ordinal();
|
||||
db.execSQL("UPDATE " + ModulesColumns.TABLE_NAME
|
||||
+ " SET " + ModulesColumns.LATEST_VERSION
|
||||
+ " = (SELECT " + ModuleVersionsColumns._ID + " FROM " + ModuleVersionsColumns.TABLE_NAME + " AS v"
|
||||
+ " WHERE v." + ModuleVersionsColumns.MODULE_ID
|
||||
+ " = " + ModulesColumns.TABLE_NAME + "." + ModulesColumns._ID
|
||||
+ " AND reltype <= ? LIMIT 1)"
|
||||
+ " WHERE " + ModulesColumns.PKGNAME + " = ?",
|
||||
new Object[]{maxShownReleaseType, packageName});
|
||||
}
|
||||
|
||||
public static void updateAllModulesLatestVersion() {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
String[] projection = new String[]{ModulesColumns.PKGNAME};
|
||||
Cursor c = db.query(true, ModulesColumns.TABLE_NAME, projection, null, null, null, null, null, null);
|
||||
while (c.moveToNext()) {
|
||||
updateModuleLatestVersion(c.getString(0));
|
||||
}
|
||||
c.close();
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static long insertInstalledModule(InstalledModule installed) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(InstalledModulesColumns.PKGNAME, installed.packageName);
|
||||
values.put(InstalledModulesColumns.VERSION_CODE, installed.versionCode);
|
||||
values.put(InstalledModulesColumns.VERSION_NAME, installed.versionName);
|
||||
return db.insertOrThrow(InstalledModulesColumns.TABLE_NAME, null, values);
|
||||
}
|
||||
|
||||
public static void deleteInstalledModule(String packageName) {
|
||||
db.delete(InstalledModulesColumns.TABLE_NAME, InstalledModulesColumns.PKGNAME + " = ?", new String[]{packageName});
|
||||
}
|
||||
|
||||
public static void deleteAllInstalledModules() {
|
||||
db.delete(InstalledModulesColumns.TABLE_NAME, null, null);
|
||||
}
|
||||
|
||||
public static Cursor queryModuleOverview(int sortingOrder,
|
||||
CharSequence filterText) {
|
||||
// Columns
|
||||
String[] projection = new String[]{
|
||||
"m." + ModulesColumns._ID,
|
||||
"m." + ModulesColumns.PKGNAME,
|
||||
"m." + ModulesColumns.TITLE,
|
||||
"m." + ModulesColumns.SUMMARY,
|
||||
"m." + ModulesColumns.CREATED,
|
||||
"m." + ModulesColumns.UPDATED,
|
||||
|
||||
"v." + ModuleVersionsColumns.NAME + " AS " + OverviewColumns.LATEST_VERSION,
|
||||
"i." + InstalledModulesColumns.VERSION_NAME + " AS " + OverviewColumns.INSTALLED_VERSION,
|
||||
|
||||
"(CASE WHEN m." + ModulesColumns.PKGNAME + " = '" + ModuleUtil.getInstance().getFrameworkPackageName()
|
||||
+ "' THEN 1 ELSE 0 END) AS " + OverviewColumns.IS_FRAMEWORK,
|
||||
|
||||
"(CASE WHEN i." + InstalledModulesColumns.VERSION_NAME + " IS NOT NULL"
|
||||
+ " THEN 1 ELSE 0 END) AS " + OverviewColumns.IS_INSTALLED,
|
||||
|
||||
"(CASE WHEN v." + ModuleVersionsColumns.CODE + " > " + InstalledModulesColumns.VERSION_CODE
|
||||
+ " THEN 1 ELSE 0 END) AS " + OverviewColumns.HAS_UPDATE,
|
||||
};
|
||||
|
||||
// Conditions
|
||||
StringBuilder where = new StringBuilder(ModulesColumns.PREFERRED + " = 1");
|
||||
String[] whereArgs = null;
|
||||
if (!TextUtils.isEmpty(filterText)) {
|
||||
where.append(" AND (m." + ModulesColumns.TITLE + " LIKE ?" + " OR m." + ModulesColumns.SUMMARY + " LIKE ?" + " OR m." + ModulesColumns.DESCRIPTION + " LIKE ?" + " OR m." + ModulesColumns.AUTHOR + " LIKE ?)");
|
||||
String filterTextArg = "%" + filterText + "%";
|
||||
whereArgs = new String[]{filterTextArg, filterTextArg, filterTextArg, filterTextArg};
|
||||
} else {
|
||||
SharedPreferences prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID + "_preferences", MODE_PRIVATE);
|
||||
|
||||
if (prefs.getBoolean("ignore_chinese", false)) {
|
||||
for (char ch : "的一是不了人我在有他这为中设微模块淘".toCharArray()) {
|
||||
where.append(" AND NOT (m." + ModulesColumns.TITLE + " LIKE '%").append(ch).append("%'").append(" OR m.").append(ModulesColumns.SUMMARY).append(" LIKE '%").append(ch).append("%'").append(" OR m.").append(ModulesColumns.DESCRIPTION).append(" LIKE '%").append(ch).append("%')");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sorting order
|
||||
StringBuilder sbOrder = new StringBuilder();
|
||||
if (sortingOrder == SORT_CREATED) {
|
||||
sbOrder.append(OverviewColumns.CREATED);
|
||||
sbOrder.append(" DESC,");
|
||||
} else if (sortingOrder == SORT_UPDATED) {
|
||||
sbOrder.append(OverviewColumns.UPDATED);
|
||||
sbOrder.append(" DESC,");
|
||||
}
|
||||
sbOrder.append(OverviewColumns.IS_FRAMEWORK);
|
||||
sbOrder.append(" DESC, ");
|
||||
sbOrder.append(OverviewColumns.HAS_UPDATE);
|
||||
sbOrder.append(" DESC, ");
|
||||
sbOrder.append(OverviewColumns.IS_INSTALLED);
|
||||
sbOrder.append(" DESC, ");
|
||||
sbOrder.append("m.");
|
||||
sbOrder.append(OverviewColumns.TITLE);
|
||||
sbOrder.append(" COLLATE NOCASE, ");
|
||||
sbOrder.append("m.");
|
||||
sbOrder.append(OverviewColumns.PKGNAME);
|
||||
|
||||
// Query
|
||||
Cursor c = db.query(
|
||||
ModulesColumns.TABLE_NAME + " AS m"
|
||||
+ " LEFT JOIN " + ModuleVersionsColumns.TABLE_NAME + " AS v"
|
||||
+ " ON v." + ModuleVersionsColumns._ID + " = m." + ModulesColumns.LATEST_VERSION
|
||||
+ " LEFT JOIN " + InstalledModulesColumns.TABLE_NAME + " AS i"
|
||||
+ " ON i." + InstalledModulesColumns.PKGNAME + " = m." + ModulesColumns.PKGNAME,
|
||||
projection, where.toString(), whereArgs, null, null, sbOrder.toString());
|
||||
|
||||
// Cache column indexes
|
||||
OverviewColumnsIndexes.fillFromCursor(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public static String getFrameworkUpdateVersion() {
|
||||
return getFirstUpdate(true);
|
||||
}
|
||||
|
||||
public static boolean hasModuleUpdates() {
|
||||
return getFirstUpdate(false) != null;
|
||||
}
|
||||
|
||||
private static String getFirstUpdate(boolean framework) {
|
||||
String[] projection = new String[]{InstalledModulesUpdatesColumns.LATEST_NAME};
|
||||
String where = ModulesColumns.PKGNAME + (framework ? " = ?" : " != ?");
|
||||
String[] whereArgs = new String[]{ModuleUtil.getInstance().getFrameworkPackageName()};
|
||||
Cursor c = db.query(InstalledModulesUpdatesColumns.VIEW_NAME, projection, where, whereArgs, null, null, null, "1");
|
||||
String latestVersion = null;
|
||||
if (c.moveToFirst())
|
||||
latestVersion = c.getString(c.getColumnIndexOrThrow(InstalledModulesUpdatesColumns.LATEST_NAME));
|
||||
c.close();
|
||||
return latestVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_TABLE_REPOSITORIES);
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_TABLE_MODULES);
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_TABLE_MODULE_VERSIONS);
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_INDEX_MODULE_VERSIONS_MODULE_ID);
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_TABLE_MORE_INFO);
|
||||
|
||||
RepoLoader.getInstance().clear(false);
|
||||
}
|
||||
|
||||
private void createTempTables(SQLiteDatabase db) {
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_TEMP_TABLE_INSTALLED_MODULES);
|
||||
db.execSQL(RepoDbDefinitions.SQL_CREATE_TEMP_VIEW_INSTALLED_MODULES_UPDATES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// This is only a cache, so simply drop & recreate the tables
|
||||
db.execSQL("DROP TABLE IF EXISTS " + RepositoriesColumns.TABLE_NAME);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + ModulesColumns.TABLE_NAME);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + ModuleVersionsColumns.TABLE_NAME);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + MoreInfoColumns.TABLE_NAME);
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS " + InstalledModulesColumns.TABLE_NAME);
|
||||
db.execSQL("DROP VIEW IF EXISTS " + InstalledModulesUpdatesColumns.VIEW_NAME);
|
||||
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
onUpgrade(db, oldVersion, newVersion);
|
||||
}
|
||||
|
||||
public static class RowNotFoundException extends RuntimeException {
|
||||
private static final long serialVersionUID = -396324186622439535L;
|
||||
|
||||
RowNotFoundException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
public class RepoDbDefinitions {
|
||||
static final int DATABASE_VERSION = 4;
|
||||
static final String DATABASE_NAME = "repo_cache.db";
|
||||
static final String SQL_CREATE_TABLE_REPOSITORIES = "CREATE TABLE "
|
||||
+ RepositoriesColumns.TABLE_NAME + " (" + RepositoriesColumns._ID
|
||||
+ " INTEGER PRIMARY KEY AUTOINCREMENT," + RepositoriesColumns.URL
|
||||
+ " TEXT NOT NULL, " + RepositoriesColumns.TITLE + " TEXT, "
|
||||
+ RepositoriesColumns.PARTIAL_URL + " TEXT, "
|
||||
+ RepositoriesColumns.VERSION + " TEXT, " + "UNIQUE ("
|
||||
+ RepositoriesColumns.URL + ") ON CONFLICT REPLACE)";
|
||||
static final String SQL_CREATE_TABLE_MODULES = "CREATE TABLE "
|
||||
+ ModulesColumns.TABLE_NAME + " (" + ModulesColumns._ID
|
||||
+ " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
|
||||
ModulesColumns.REPO_ID + " INTEGER NOT NULL REFERENCES "
|
||||
+ RepositoriesColumns.TABLE_NAME + " ON DELETE CASCADE, "
|
||||
+ ModulesColumns.PKGNAME + " TEXT NOT NULL, " + ModulesColumns.TITLE
|
||||
+ " TEXT NOT NULL, " + ModulesColumns.SUMMARY + " TEXT, "
|
||||
+ ModulesColumns.DESCRIPTION + " TEXT, "
|
||||
+ ModulesColumns.DESCRIPTION_IS_HTML + " INTEGER DEFAULT 0, "
|
||||
+ ModulesColumns.AUTHOR + " TEXT, " + ModulesColumns.SUPPORT
|
||||
+ " TEXT, " + ModulesColumns.CREATED + " INTEGER DEFAULT -1, "
|
||||
+ ModulesColumns.UPDATED + " INTEGER DEFAULT -1, "
|
||||
+ ModulesColumns.PREFERRED + " INTEGER DEFAULT 1, "
|
||||
+ ModulesColumns.LATEST_VERSION + " INTEGER REFERENCES "
|
||||
+ ModuleVersionsColumns.TABLE_NAME + ", " + "UNIQUE ("
|
||||
+ ModulesColumns.PKGNAME + ", " + ModulesColumns.REPO_ID
|
||||
+ ") ON CONFLICT REPLACE)";
|
||||
static final String SQL_CREATE_TABLE_MODULE_VERSIONS = "CREATE TABLE "
|
||||
+ ModuleVersionsColumns.TABLE_NAME + " ("
|
||||
+ ModuleVersionsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
+ ModuleVersionsColumns.MODULE_ID + " INTEGER NOT NULL REFERENCES "
|
||||
+ ModulesColumns.TABLE_NAME + " ON DELETE CASCADE, "
|
||||
+ ModuleVersionsColumns.NAME + " TEXT NOT NULL, "
|
||||
+ ModuleVersionsColumns.CODE + " INTEGER NOT NULL, "
|
||||
+ ModuleVersionsColumns.DOWNLOAD_LINK + " TEXT, "
|
||||
+ ModuleVersionsColumns.MD5SUM + " TEXT, "
|
||||
+ ModuleVersionsColumns.CHANGELOG + " TEXT, "
|
||||
+ ModuleVersionsColumns.CHANGELOG_IS_HTML + " INTEGER DEFAULT 0, "
|
||||
+ ModuleVersionsColumns.RELTYPE + " INTEGER DEFAULT 0, "
|
||||
+ ModuleVersionsColumns.UPLOADED + " INTEGER DEFAULT -1)";
|
||||
static final String SQL_CREATE_INDEX_MODULE_VERSIONS_MODULE_ID = "CREATE INDEX "
|
||||
+ ModuleVersionsColumns.IDX_MODULE_ID + " ON "
|
||||
+ ModuleVersionsColumns.TABLE_NAME + " ("
|
||||
+ ModuleVersionsColumns.MODULE_ID + ")";
|
||||
static final String SQL_CREATE_TABLE_MORE_INFO = "CREATE TABLE "
|
||||
+ MoreInfoColumns.TABLE_NAME + " (" + MoreInfoColumns._ID
|
||||
+ " INTEGER PRIMARY KEY AUTOINCREMENT," + MoreInfoColumns.MODULE_ID
|
||||
+ " INTEGER NOT NULL REFERENCES " + ModulesColumns.TABLE_NAME
|
||||
+ " ON DELETE CASCADE, " + MoreInfoColumns.LABEL
|
||||
+ " TEXT NOT NULL, " + MoreInfoColumns.VALUE + " TEXT)";
|
||||
static final String SQL_CREATE_TEMP_TABLE_INSTALLED_MODULES = "CREATE TEMP TABLE "
|
||||
+ InstalledModulesColumns.TABLE_NAME + " ("
|
||||
+ InstalledModulesColumns.PKGNAME
|
||||
+ " TEXT PRIMARY KEY ON CONFLICT REPLACE, "
|
||||
+ InstalledModulesColumns.VERSION_CODE + " INTEGER NOT NULL, "
|
||||
+ InstalledModulesColumns.VERSION_NAME + " TEXT)";
|
||||
static final String SQL_CREATE_TEMP_VIEW_INSTALLED_MODULES_UPDATES = "CREATE TEMP VIEW "
|
||||
+ InstalledModulesUpdatesColumns.VIEW_NAME + " AS SELECT " + "m."
|
||||
+ ModulesColumns._ID + " AS "
|
||||
+ InstalledModulesUpdatesColumns.MODULE_ID + ", " + "i."
|
||||
+ InstalledModulesColumns.PKGNAME + " AS "
|
||||
+ InstalledModulesUpdatesColumns.PKGNAME + ", " + "i."
|
||||
+ InstalledModulesColumns.VERSION_CODE + " AS "
|
||||
+ InstalledModulesUpdatesColumns.INSTALLED_CODE + ", " + "i."
|
||||
+ InstalledModulesColumns.VERSION_NAME + " AS "
|
||||
+ InstalledModulesUpdatesColumns.INSTALLED_NAME + ", " + "v."
|
||||
+ ModuleVersionsColumns._ID + " AS "
|
||||
+ InstalledModulesUpdatesColumns.LATEST_ID + ", " + "v."
|
||||
+ ModuleVersionsColumns.CODE + " AS "
|
||||
+ InstalledModulesUpdatesColumns.LATEST_CODE + ", " + "v."
|
||||
+ ModuleVersionsColumns.NAME + " AS "
|
||||
+ InstalledModulesUpdatesColumns.LATEST_NAME + " FROM "
|
||||
+ InstalledModulesColumns.TABLE_NAME + " AS i" + " INNER JOIN "
|
||||
+ ModulesColumns.TABLE_NAME + " AS m" + " ON m."
|
||||
+ ModulesColumns.PKGNAME + " = i." + InstalledModulesColumns.PKGNAME
|
||||
+ " INNER JOIN " + ModuleVersionsColumns.TABLE_NAME + " AS v"
|
||||
+ " ON v." + ModuleVersionsColumns._ID + " = m."
|
||||
+ ModulesColumns.LATEST_VERSION + " WHERE "
|
||||
+ InstalledModulesUpdatesColumns.LATEST_CODE + " > "
|
||||
+ InstalledModulesUpdatesColumns.INSTALLED_CODE + " AND "
|
||||
+ ModulesColumns.PREFERRED + " = 1";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface RepositoriesColumns extends BaseColumns {
|
||||
String TABLE_NAME = "repositories";
|
||||
|
||||
String URL = "url";
|
||||
String TITLE = "title";
|
||||
String PARTIAL_URL = "partial_url";
|
||||
String VERSION = "version";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface ModulesColumns extends BaseColumns {
|
||||
String TABLE_NAME = "modules";
|
||||
|
||||
String REPO_ID = "repo_id";
|
||||
String PKGNAME = "pkgname";
|
||||
String TITLE = "title";
|
||||
String SUMMARY = "summary";
|
||||
String DESCRIPTION = "description";
|
||||
String DESCRIPTION_IS_HTML = "description_is_html";
|
||||
String AUTHOR = "author";
|
||||
String SUPPORT = "support";
|
||||
String CREATED = "created";
|
||||
String UPDATED = "updated";
|
||||
|
||||
String PREFERRED = "preferred";
|
||||
String LATEST_VERSION = "latest_version_id";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface ModuleVersionsColumns extends BaseColumns {
|
||||
String TABLE_NAME = "module_versions";
|
||||
String IDX_MODULE_ID = "module_versions_module_id_idx";
|
||||
|
||||
String MODULE_ID = "module_id";
|
||||
String NAME = "name";
|
||||
String CODE = "code";
|
||||
String DOWNLOAD_LINK = "download_link";
|
||||
String MD5SUM = "md5sum";
|
||||
String CHANGELOG = "changelog";
|
||||
String CHANGELOG_IS_HTML = "changelog_is_html";
|
||||
String RELTYPE = "reltype";
|
||||
String UPLOADED = "uploaded";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface MoreInfoColumns extends BaseColumns {
|
||||
String TABLE_NAME = "more_info";
|
||||
|
||||
String MODULE_ID = "module_id";
|
||||
String LABEL = "label";
|
||||
String VALUE = "value";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface InstalledModulesColumns {
|
||||
String TABLE_NAME = "installed_modules";
|
||||
|
||||
String PKGNAME = "pkgname";
|
||||
String VERSION_CODE = "version_code";
|
||||
String VERSION_NAME = "version_name";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface InstalledModulesUpdatesColumns {
|
||||
String VIEW_NAME = InstalledModulesColumns.TABLE_NAME + "_updates";
|
||||
|
||||
String MODULE_ID = "module_id";
|
||||
String PKGNAME = "pkgname";
|
||||
String INSTALLED_CODE = "installed_code";
|
||||
String INSTALLED_NAME = "installed_name";
|
||||
String LATEST_ID = "latest_id";
|
||||
String LATEST_CODE = "latest_code";
|
||||
String LATEST_NAME = "latest_name";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
public interface OverviewColumns extends BaseColumns {
|
||||
String PKGNAME = ModulesColumns.PKGNAME;
|
||||
String TITLE = ModulesColumns.TITLE;
|
||||
String SUMMARY = ModulesColumns.SUMMARY;
|
||||
String CREATED = ModulesColumns.CREATED;
|
||||
String UPDATED = ModulesColumns.UPDATED;
|
||||
|
||||
String INSTALLED_VERSION = "installed_version";
|
||||
String LATEST_VERSION = "latest_version";
|
||||
|
||||
String IS_FRAMEWORK = "is_framework";
|
||||
String IS_INSTALLED = "is_installed";
|
||||
String HAS_UPDATE = "has_update";
|
||||
}
|
||||
|
||||
public static class OverviewColumnsIndexes {
|
||||
public static int PKGNAME = -1;
|
||||
public static int TITLE = -1;
|
||||
public static int SUMMARY = -1;
|
||||
public static int CREATED = -1;
|
||||
public static int UPDATED = -1;
|
||||
public static int INSTALLED_VERSION = -1;
|
||||
public static int LATEST_VERSION = -1;
|
||||
public static int IS_FRAMEWORK = -1;
|
||||
public static int IS_INSTALLED = -1;
|
||||
public static int HAS_UPDATE = -1;
|
||||
private static boolean isFilled = false;
|
||||
|
||||
private OverviewColumnsIndexes() {
|
||||
}
|
||||
|
||||
static void fillFromCursor(Cursor cursor) {
|
||||
if (isFilled || cursor == null)
|
||||
return;
|
||||
|
||||
PKGNAME = cursor.getColumnIndexOrThrow(OverviewColumns.PKGNAME);
|
||||
TITLE = cursor.getColumnIndexOrThrow(OverviewColumns.TITLE);
|
||||
SUMMARY = cursor.getColumnIndexOrThrow(OverviewColumns.SUMMARY);
|
||||
CREATED = cursor.getColumnIndexOrThrow(OverviewColumns.CREATED);
|
||||
UPDATED = cursor.getColumnIndexOrThrow(OverviewColumns.UPDATED);
|
||||
INSTALLED_VERSION = cursor.getColumnIndexOrThrow(OverviewColumns.INSTALLED_VERSION);
|
||||
LATEST_VERSION = cursor.getColumnIndexOrThrow(OverviewColumns.LATEST_VERSION);
|
||||
INSTALLED_VERSION = cursor.getColumnIndexOrThrow(OverviewColumns.INSTALLED_VERSION);
|
||||
IS_FRAMEWORK = cursor.getColumnIndexOrThrow(OverviewColumns.IS_FRAMEWORK);
|
||||
IS_INSTALLED = cursor.getColumnIndexOrThrow(OverviewColumns.IS_INSTALLED);
|
||||
HAS_UPDATE = cursor.getColumnIndexOrThrow(OverviewColumns.HAS_UPDATE);
|
||||
|
||||
isFilled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LevelListDrawable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import org.meowcat.edxposed.manager.App;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class RepoParser {
|
||||
public final static String TAG = App.TAG;
|
||||
private final static String NS = null;
|
||||
private final XmlPullParser parser;
|
||||
private final RepoParserCallback callback;
|
||||
private boolean mRepoEventTriggered = false;
|
||||
|
||||
private RepoParser(InputStream is, RepoParserCallback callback) throws XmlPullParserException, IOException {
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
parser = factory.newPullParser();
|
||||
parser.setInput(is, null);
|
||||
parser.nextTag();
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public static void parse(InputStream is, RepoParserCallback callback) throws XmlPullParserException, IOException {
|
||||
new RepoParser(is, callback).readRepo();
|
||||
}
|
||||
|
||||
public static Spanned parseSimpleHtml(final Context context, String source, final TextView textView) {
|
||||
source = source.replaceAll("<li>", "\t\u0095 ");
|
||||
source = source.replaceAll("</li>", "<br>");
|
||||
Spanned html = HtmlCompat.fromHtml(source, HtmlCompat.FROM_HTML_MODE_LEGACY, source1 -> {
|
||||
|
||||
LevelListDrawable levelListDrawable = new LevelListDrawable();
|
||||
final Drawable[] drawable = new Drawable[1];
|
||||
Glide.with(context).asBitmap().load(source1).into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition<? super Bitmap> transition) {
|
||||
try {
|
||||
drawable[0] = new BitmapDrawable(context.getResources(), bitmap);
|
||||
Point size = new Point();
|
||||
((Activity) context).getWindowManager().getDefaultDisplay().getSize(size);
|
||||
int multiplier = size.x / bitmap.getWidth();
|
||||
if (multiplier <= 0) multiplier = 1;
|
||||
levelListDrawable.addLevel(1, 1, drawable[0]);
|
||||
levelListDrawable.setBounds(0, 0, bitmap.getWidth() * multiplier, bitmap.getHeight() * multiplier);
|
||||
levelListDrawable.setLevel(1);
|
||||
textView.setText(textView.getText());
|
||||
} catch (Exception ignored) { /* Like a null bitmap, etc. */
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
}
|
||||
});
|
||||
return drawable[0];
|
||||
}, null);
|
||||
|
||||
// trim trailing newlines
|
||||
int len = html.length();
|
||||
int end = len;
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
if (html.charAt(i) != '\n')
|
||||
break;
|
||||
end = i;
|
||||
}
|
||||
|
||||
if (end == len)
|
||||
return html;
|
||||
else
|
||||
return new SpannableStringBuilder(html, 0, end);
|
||||
}
|
||||
|
||||
private void readRepo() throws XmlPullParserException, IOException {
|
||||
parser.require(XmlPullParser.START_TAG, NS, "repository");
|
||||
Repository repository = new Repository();
|
||||
repository.isPartial = "true".equals(parser.getAttributeValue(NS, "partial"));
|
||||
repository.partialUrl = parser.getAttributeValue(NS, "partial-url");
|
||||
repository.version = parser.getAttributeValue(NS, "version");
|
||||
|
||||
while (parser.nextTag() == XmlPullParser.START_TAG) {
|
||||
String tagName = parser.getName();
|
||||
switch (tagName) {
|
||||
case "name":
|
||||
repository.name = parser.nextText();
|
||||
break;
|
||||
case "module":
|
||||
triggerRepoEvent(repository);
|
||||
Module module = readModule(repository);
|
||||
if (module != null)
|
||||
callback.onNewModule(module);
|
||||
break;
|
||||
case "remove-module":
|
||||
triggerRepoEvent(repository);
|
||||
String packageName = readRemoveModule();
|
||||
if (packageName != null)
|
||||
callback.onRemoveModule(packageName);
|
||||
break;
|
||||
default:
|
||||
//skip(true);
|
||||
skip(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
callback.onCompleted(repository);
|
||||
}
|
||||
|
||||
private void triggerRepoEvent(Repository repository) {
|
||||
if (mRepoEventTriggered)
|
||||
return;
|
||||
|
||||
callback.onRepositoryMetadata(repository);
|
||||
mRepoEventTriggered = true;
|
||||
}
|
||||
|
||||
private Module readModule(Repository repository) throws XmlPullParserException, IOException {
|
||||
parser.require(XmlPullParser.START_TAG, NS, "module");
|
||||
final int startDepth = parser.getDepth();
|
||||
|
||||
Module module = new Module(repository);
|
||||
module.packageName = parser.getAttributeValue(NS, "package");
|
||||
if (module.packageName == null) {
|
||||
logError("no package name defined");
|
||||
leave(startDepth);
|
||||
return null;
|
||||
}
|
||||
|
||||
module.created = parseTimestamp("created");
|
||||
module.updated = parseTimestamp("updated");
|
||||
|
||||
while (parser.nextTag() == XmlPullParser.START_TAG) {
|
||||
String tagName = parser.getName();
|
||||
switch (tagName) {
|
||||
case "name":
|
||||
module.name = parser.nextText();
|
||||
break;
|
||||
case "author":
|
||||
module.author = parser.nextText();
|
||||
break;
|
||||
case "summary":
|
||||
module.summary = parser.nextText();
|
||||
break;
|
||||
case "description":
|
||||
String isHtml = parser.getAttributeValue(NS, "html");
|
||||
if (isHtml != null && isHtml.equals("true"))
|
||||
module.descriptionIsHtml = true;
|
||||
module.description = parser.nextText();
|
||||
break;
|
||||
case "screenshot":
|
||||
module.screenshots.add(parser.nextText());
|
||||
break;
|
||||
case "moreinfo":
|
||||
String label = parser.getAttributeValue(NS, "label");
|
||||
String role = parser.getAttributeValue(NS, "role");
|
||||
String value = parser.nextText();
|
||||
module.moreInfo.add(new Pair<>(label, value));
|
||||
|
||||
if (role != null && role.contains("support"))
|
||||
module.support = value;
|
||||
break;
|
||||
case "version":
|
||||
ModuleVersion version = readModuleVersion(module);
|
||||
if (version != null)
|
||||
module.versions.add(version);
|
||||
break;
|
||||
default:
|
||||
//skip(true);
|
||||
skip(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (module.name == null) {
|
||||
logError("packages need at least a name");
|
||||
return null;
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
private long parseTimestamp(String attName) {
|
||||
String value = parser.getAttributeValue(NS, attName);
|
||||
if (value == null)
|
||||
return -1;
|
||||
try {
|
||||
return Long.parseLong(value) * 1000L;
|
||||
} catch (NumberFormatException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private ModuleVersion readModuleVersion(Module module) throws XmlPullParserException, IOException {
|
||||
parser.require(XmlPullParser.START_TAG, NS, "version");
|
||||
final int startDepth = parser.getDepth();
|
||||
ModuleVersion version = new ModuleVersion(module);
|
||||
|
||||
version.uploaded = parseTimestamp("uploaded");
|
||||
|
||||
while (parser.nextTag() == XmlPullParser.START_TAG) {
|
||||
String tagName = parser.getName();
|
||||
switch (tagName) {
|
||||
case "name":
|
||||
version.name = parser.nextText();
|
||||
break;
|
||||
case "code":
|
||||
try {
|
||||
version.code = Integer.parseInt(parser.nextText());
|
||||
} catch (NumberFormatException nfe) {
|
||||
logError(nfe.getMessage());
|
||||
leave(startDepth);
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
case "reltype":
|
||||
version.relType = ReleaseType.fromString(parser.nextText());
|
||||
break;
|
||||
case "download":
|
||||
version.downloadLink = parser.nextText();
|
||||
break;
|
||||
case "md5sum":
|
||||
version.md5sum = parser.nextText();
|
||||
break;
|
||||
case "changelog":
|
||||
String isHtml = parser.getAttributeValue(NS, "html");
|
||||
if (isHtml != null && isHtml.equals("true"))
|
||||
version.changelogIsHtml = true;
|
||||
version.changelog = parser.nextText();
|
||||
break;
|
||||
case "branch":
|
||||
// obsolete
|
||||
// skip(false);
|
||||
// break;
|
||||
default:
|
||||
skip(false);
|
||||
//skip(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
private String readRemoveModule() throws XmlPullParserException, IOException {
|
||||
parser.require(XmlPullParser.START_TAG, NS, "remove-module");
|
||||
final int startDepth = parser.getDepth();
|
||||
|
||||
String packageName = parser.getAttributeValue(NS, "package");
|
||||
if (packageName == null) {
|
||||
logError("no package name defined");
|
||||
leave(startDepth);
|
||||
return null;
|
||||
}
|
||||
|
||||
return packageName;
|
||||
}
|
||||
|
||||
private void skip(@SuppressWarnings("SameParameterValue") boolean showWarning) throws XmlPullParserException, IOException {
|
||||
parser.require(XmlPullParser.START_TAG, null, null);
|
||||
if (showWarning)
|
||||
Log.w(TAG, "skipping unknown/erronous tag: " + parser.getPositionDescription());
|
||||
int level = 1;
|
||||
while (level > 0) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.END_TAG) {
|
||||
level--;
|
||||
} else if (eventType == XmlPullParser.START_TAG) {
|
||||
level++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void leave(int targetDepth) throws XmlPullParserException, IOException {
|
||||
Log.w(TAG, "leaving up to level " + targetDepth + ": " + parser.getPositionDescription());
|
||||
while (parser.getDepth() > targetDepth) {
|
||||
//noinspection StatementWithEmptyBody
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logError(String error) {
|
||||
Log.e(TAG, parser.getPositionDescription() + ": " + error);
|
||||
}
|
||||
|
||||
public interface RepoParserCallback {
|
||||
void onRepositoryMetadata(Repository repository);
|
||||
|
||||
void onNewModule(Module module);
|
||||
|
||||
void onRemoveModule(String packageName);
|
||||
|
||||
void onCompleted(Repository repository);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.repo;
|
||||
|
||||
public class Repository {
|
||||
public String name;
|
||||
public String url;
|
||||
public boolean isPartial = false;
|
||||
public String partialUrl;
|
||||
public String version;
|
||||
|
||||
Repository() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,398 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.ui.activity;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter;
|
||||
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration;
|
||||
|
||||
import org.meowcat.edxposed.manager.App;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.adapters.CursorRecyclerViewAdapter;
|
||||
import org.meowcat.edxposed.manager.databinding.ActivityDownloadBinding;
|
||||
import org.meowcat.edxposed.manager.databinding.ItemDownloadBinding;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDb;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDbDefinitions;
|
||||
import org.meowcat.edxposed.manager.util.LinearLayoutManagerFix;
|
||||
import org.meowcat.edxposed.manager.util.ModuleUtil;
|
||||
import org.meowcat.edxposed.manager.util.RepoLoader;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder;
|
||||
|
||||
public class DownloadActivity extends BaseActivity implements RepoLoader.RepoListener, ModuleUtil.ModuleListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private DownloadsAdapter adapter;
|
||||
private String filterText;
|
||||
private RepoLoader repoLoader;
|
||||
private ModuleUtil moduleUtil;
|
||||
private int sortingOrder;
|
||||
private SearchView searchView;
|
||||
private SharedPreferences ignoredUpdatesPref;
|
||||
private boolean changed = false;
|
||||
private final BroadcastReceiver connectionListener = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (repoLoader != null) {
|
||||
repoLoader.triggerReload(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
private ActivityDownloadBinding binding;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityDownloadBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
setSupportActionBar(binding.toolbar);
|
||||
binding.toolbar.setNavigationOnClickListener(view -> finish());
|
||||
ActionBar bar = getSupportActionBar();
|
||||
if (bar != null) {
|
||||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
setupWindowInsets(binding.snackbar, binding.recyclerView);
|
||||
repoLoader = RepoLoader.getInstance();
|
||||
moduleUtil = ModuleUtil.getInstance();
|
||||
adapter = new DownloadsAdapter(this, RepoDb.queryModuleOverview(sortingOrder, filterText));
|
||||
/*adapter.setFilterQueryProvider(new FilterQueryProvider() {
|
||||
@Override
|
||||
public Cursor runQuery(CharSequence constraint) {
|
||||
return RepoDb.queryModuleOverview(sortingOrder, constraint);
|
||||
}
|
||||
});*/
|
||||
|
||||
sortingOrder = App.getPreferences().getInt("download_sorting_order", RepoDb.SORT_STATUS);
|
||||
|
||||
ignoredUpdatesPref = getSharedPreferences("update_ignored", MODE_PRIVATE);
|
||||
binding.recyclerView.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
repoLoader.setSwipeRefreshLayout(binding.swipeRefreshLayout);
|
||||
repoLoader.triggerReload(true);
|
||||
});
|
||||
repoLoader.addListener(this, true);
|
||||
moduleUtil.addListener(this);
|
||||
binding.recyclerView.setAdapter(adapter);
|
||||
|
||||
binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(this));
|
||||
StickyRecyclerHeadersDecoration headersDecor = new StickyRecyclerHeadersDecoration(adapter);
|
||||
binding.recyclerView.addItemDecoration(headersDecor);
|
||||
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
headersDecor.invalidateHeaders();
|
||||
}
|
||||
});
|
||||
FastScrollerBuilder fastScrollerBuilder = new FastScrollerBuilder(binding.recyclerView);
|
||||
if (!App.getPreferences().getBoolean("md2", false)) {
|
||||
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this,
|
||||
DividerItemDecoration.VERTICAL);
|
||||
binding.recyclerView.addItemDecoration(dividerItemDecoration);
|
||||
} else {
|
||||
fastScrollerBuilder.useMd2Style();
|
||||
}
|
||||
fastScrollerBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
ignoredUpdatesPref.registerOnSharedPreferenceChangeListener(this);
|
||||
if (changed) {
|
||||
reloadItems();
|
||||
changed = !changed;
|
||||
}
|
||||
|
||||
registerReceiver(connectionListener, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
unregisterReceiver(connectionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
repoLoader.removeListener(this);
|
||||
moduleUtil.removeListener(this);
|
||||
ignoredUpdatesPref.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_download, menu);
|
||||
|
||||
// Setup search button
|
||||
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
|
||||
searchView.setIconifiedByDefault(true);
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
setFilter(query);
|
||||
searchView.clearFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
setFilter(newText);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
private void setFilter(String filterText) {
|
||||
this.filterText = filterText;
|
||||
reloadItems();
|
||||
}
|
||||
|
||||
private void reloadItems() {
|
||||
runOnUiThread(() -> {
|
||||
adapter.changeCursor(RepoDb.queryModuleOverview(sortingOrder, filterText));
|
||||
TransitionManager.beginDelayedTransition(binding.recyclerView);
|
||||
adapter.notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_sort) {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.download_sorting_title)
|
||||
.setSingleChoiceItems(R.array.download_sort_order, sortingOrder, (dialog, which) -> {
|
||||
sortingOrder = which;
|
||||
App.getPreferences().edit().putInt("download_sorting_order", sortingOrder).apply();
|
||||
reloadItems();
|
||||
dialog.dismiss();
|
||||
})
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepoReloaded(final RepoLoader loader) {
|
||||
reloadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, ModuleUtil.InstalledModule module) {
|
||||
reloadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstalledModulesReloaded(ModuleUtil moduleUtil) {
|
||||
reloadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModuleEnableChange(ModuleUtil moduleUtil) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (searchView.isIconified()) {
|
||||
super.onBackPressed();
|
||||
} else {
|
||||
searchView.setIconified(true);
|
||||
}
|
||||
}
|
||||
|
||||
private class DownloadsAdapter extends CursorRecyclerViewAdapter<DownloadsAdapter.ViewHolder> implements StickyRecyclerHeadersAdapter<DownloadsAdapter.HeaderViewHolder> {
|
||||
private final Context context;
|
||||
private final DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT);
|
||||
private final SharedPreferences prefs;
|
||||
private final String[] sectionHeaders;
|
||||
|
||||
DownloadsAdapter(Context context, Cursor cursor) {
|
||||
super(cursor);
|
||||
this.context = context;
|
||||
prefs = context.getSharedPreferences("update_ignored", MODE_PRIVATE);
|
||||
|
||||
Resources res = context.getResources();
|
||||
sectionHeaders = new String[]{
|
||||
res.getString(R.string.download_section_framework),
|
||||
res.getString(R.string.download_section_update_available),
|
||||
res.getString(R.string.download_section_installed),
|
||||
res.getString(R.string.download_section_not_installed),
|
||||
res.getString(R.string.download_section_24h),
|
||||
res.getString(R.string.download_section_7d),
|
||||
res.getString(R.string.download_section_30d),
|
||||
res.getString(R.string.download_section_older)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHeaderId(int position) {
|
||||
Cursor cursor = getCursor();
|
||||
cursor.moveToPosition(position);
|
||||
long created = cursor.getLong(RepoDbDefinitions.OverviewColumnsIndexes.CREATED);
|
||||
long updated = cursor.getLong(RepoDbDefinitions.OverviewColumnsIndexes.UPDATED);
|
||||
boolean isFramework = cursor.getInt(RepoDbDefinitions.OverviewColumnsIndexes.IS_FRAMEWORK) > 0;
|
||||
boolean isInstalled = cursor.getInt(RepoDbDefinitions.OverviewColumnsIndexes.IS_INSTALLED) > 0;
|
||||
boolean updateIgnored = prefs.getBoolean(cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.PKGNAME), false);
|
||||
boolean updateIgnorePreference = App.getPreferences().getBoolean("ignore_updates", false);
|
||||
boolean hasUpdate = cursor.getInt(RepoDbDefinitions.OverviewColumnsIndexes.HAS_UPDATE) > 0;
|
||||
|
||||
if (hasUpdate && updateIgnored && updateIgnorePreference) {
|
||||
hasUpdate = false;
|
||||
}
|
||||
|
||||
if (sortingOrder != RepoDb.SORT_STATUS) {
|
||||
long timestamp = (sortingOrder == RepoDb.SORT_UPDATED) ? updated : created;
|
||||
long age = System.currentTimeMillis() - timestamp;
|
||||
final long mSecsPerDay = 24 * 60 * 60 * 1000L;
|
||||
if (age < mSecsPerDay)
|
||||
return 4;
|
||||
if (age < 7 * mSecsPerDay)
|
||||
return 5;
|
||||
if (age < 30 * mSecsPerDay)
|
||||
return 6;
|
||||
return 7;
|
||||
} else {
|
||||
if (isFramework)
|
||||
return 0;
|
||||
|
||||
if (hasUpdate)
|
||||
return 1;
|
||||
else if (isInstalled)
|
||||
return 2;
|
||||
else
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sticky_header_download, parent, false);
|
||||
return new HeaderViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||
long section = getHeaderId(position);
|
||||
viewHolder.title.setText(sectionHeaders[(int) section]);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemDownloadBinding binding = ItemDownloadBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, Cursor cursor) {
|
||||
String title = cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.TITLE);
|
||||
String summary = cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.SUMMARY);
|
||||
String installedVersion = cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.INSTALLED_VERSION);
|
||||
String latestVersion = cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.LATEST_VERSION);
|
||||
long created = cursor.getLong(RepoDbDefinitions.OverviewColumnsIndexes.CREATED);
|
||||
long updated = cursor.getLong(RepoDbDefinitions.OverviewColumnsIndexes.UPDATED);
|
||||
boolean isInstalled = cursor.getInt(RepoDbDefinitions.OverviewColumnsIndexes.IS_INSTALLED) > 0;
|
||||
boolean updateIgnored = prefs.getBoolean(cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.PKGNAME), false);
|
||||
boolean updateIgnorePreference = App.getPreferences().getBoolean("ignore_updates", false);
|
||||
boolean hasUpdate = cursor.getInt(RepoDbDefinitions.OverviewColumnsIndexes.HAS_UPDATE) > 0;
|
||||
|
||||
if (hasUpdate && updateIgnored && updateIgnorePreference) {
|
||||
hasUpdate = false;
|
||||
}
|
||||
|
||||
TextView txtTitle = holder.appName;
|
||||
txtTitle.setText(title);
|
||||
|
||||
TextView txtSummary = holder.appDescription;
|
||||
txtSummary.setText(summary);
|
||||
|
||||
TextView txtStatus = holder.downloadStatus;
|
||||
if (hasUpdate) {
|
||||
txtStatus.setText(context.getString(
|
||||
R.string.download_status_update_available,
|
||||
installedVersion, latestVersion));
|
||||
txtStatus.setTextColor(ContextCompat.getColor(DownloadActivity.this, R.color.download_status_update_available));
|
||||
txtStatus.setVisibility(View.VISIBLE);
|
||||
} else if (isInstalled) {
|
||||
txtStatus.setText(context.getString(
|
||||
R.string.download_status_installed, installedVersion));
|
||||
txtStatus.setTextColor(ContextCompat.getColor(DownloadActivity.this, R.color.warning));
|
||||
txtStatus.setTextColor(getThemedColor(android.R.attr.textColorHighlight));
|
||||
txtStatus.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
txtStatus.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
String creationDate = dateFormatter.format(new Date(created));
|
||||
String updateDate = dateFormatter.format(new Date(updated));
|
||||
holder.timestamps.setText(getString(R.string.download_timestamps, creationDate, updateDate));
|
||||
String packageName = cursor.getString(RepoDbDefinitions.OverviewColumnsIndexes.PKGNAME);
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
Intent detailsIntent = new Intent(DownloadActivity.this, DownloadDetailsActivity.class);
|
||||
detailsIntent.setData(Uri.fromParts("package", packageName, null));
|
||||
startActivity(detailsIntent);
|
||||
});
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView appName;
|
||||
TextView appDescription;
|
||||
TextView downloadStatus;
|
||||
TextView timestamps;
|
||||
|
||||
ViewHolder(ItemDownloadBinding binding) {
|
||||
super(binding.getRoot());
|
||||
appName = binding.title;
|
||||
appDescription = binding.description;
|
||||
downloadStatus = binding.downloadStatus;
|
||||
timestamps = binding.timestamps;
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView title;
|
||||
|
||||
HeaderViewHolder(View view) {
|
||||
super(view);
|
||||
title = view.findViewById(android.R.id.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.ui.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import org.meowcat.edxposed.manager.App;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.databinding.ActivityDownloadDetailsBinding;
|
||||
import org.meowcat.edxposed.manager.databinding.ActivityDownloadDetailsNotFoundBinding;
|
||||
import org.meowcat.edxposed.manager.repo.Module;
|
||||
import org.meowcat.edxposed.manager.ui.fragment.DownloadDetailsFragment;
|
||||
import org.meowcat.edxposed.manager.ui.fragment.DownloadDetailsSettingsFragment;
|
||||
import org.meowcat.edxposed.manager.ui.fragment.DownloadDetailsVersionsFragment;
|
||||
import org.meowcat.edxposed.manager.util.ModuleUtil;
|
||||
import org.meowcat.edxposed.manager.util.RepoLoader;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DownloadDetailsActivity extends BaseActivity implements RepoLoader.RepoListener, ModuleUtil.ModuleListener {
|
||||
|
||||
public static final int DOWNLOAD_DESCRIPTION = 0;
|
||||
public static final int DOWNLOAD_VERSIONS = 1;
|
||||
public static final int DOWNLOAD_SETTINGS = 2;
|
||||
static final String XPOSED_REPO_LINK = "http://repo.xposed.info/module/%s";
|
||||
static final String PLAY_STORE_PACKAGE = "com.android.vending";
|
||||
static final String PLAY_STORE_LINK = "https://play.google.com/store/apps/details?id=%s";
|
||||
private static final String TAG = "DownloadDetailsActivity";
|
||||
private static final RepoLoader repoLoader = RepoLoader.getInstance();
|
||||
private static final ModuleUtil moduleUtil = ModuleUtil.getInstance();
|
||||
private String packageName;
|
||||
private Module module;
|
||||
private ModuleUtil.InstalledModule installedModule;
|
||||
private ActivityDownloadDetailsBinding binding;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
packageName = getModulePackageName();
|
||||
try {
|
||||
module = repoLoader.getModule(packageName);
|
||||
} catch (Exception e) {
|
||||
Log.i(App.TAG, "DownloadDetailsActivity -> " + e.getMessage());
|
||||
|
||||
module = null;
|
||||
}
|
||||
|
||||
installedModule = ModuleUtil.getInstance().getModule(packageName);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
repoLoader.addListener(this, false);
|
||||
moduleUtil.addListener(this);
|
||||
|
||||
if (module != null) {
|
||||
binding = ActivityDownloadDetailsBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbar);
|
||||
binding.toolbar.setNavigationOnClickListener(view -> finish());
|
||||
|
||||
ActionBar ab = getSupportActionBar();
|
||||
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
setupTabs();
|
||||
|
||||
boolean directDownload = getIntent().getBooleanExtra("direct_download", false);
|
||||
// Updates available => start on the versions page
|
||||
if (installedModule != null && installedModule.isUpdate(repoLoader.getLatestVersion(module)) || directDownload)
|
||||
binding.downloadPager.setCurrentItem(DOWNLOAD_VERSIONS);
|
||||
|
||||
} else {
|
||||
ActivityDownloadDetailsNotFoundBinding binding = ActivityDownloadDetailsNotFoundBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.message.setText(getResources().getString(R.string.download_details_not_found, packageName));
|
||||
|
||||
binding.reload.setOnClickListener(v -> {
|
||||
v.setEnabled(false);
|
||||
repoLoader.triggerReload(true);
|
||||
});
|
||||
}
|
||||
setupWindowInsets(binding.snackbar, null);
|
||||
}
|
||||
|
||||
private void setupTabs() {
|
||||
binding.downloadPager.setAdapter(new SwipeFragmentPagerAdapter(getSupportFragmentManager()));
|
||||
binding.slidingTabs.setupWithViewPager(binding.downloadPager);
|
||||
}
|
||||
|
||||
private String getModulePackageName() {
|
||||
Uri uri = getIntent().getData();
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
String scheme = uri.getScheme();
|
||||
if (TextUtils.isEmpty(scheme)) {
|
||||
return null;
|
||||
} else switch (Objects.requireNonNull(scheme)) {
|
||||
case "xposed":
|
||||
case "package":
|
||||
return uri.getSchemeSpecificPart().replace("//", "");
|
||||
case "http":
|
||||
List<String> segments = uri.getPathSegments();
|
||||
if (segments.size() > 1)
|
||||
return segments.get(1);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
repoLoader.removeListener(this);
|
||||
moduleUtil.removeListener(this);
|
||||
}
|
||||
|
||||
public Module getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public ModuleUtil.InstalledModule getInstalledModule() {
|
||||
return installedModule;
|
||||
}
|
||||
|
||||
public void gotoPage(int page) {
|
||||
binding.downloadPager.setCurrentItem(page);
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
runOnUiThread(this::recreate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepoReloaded(RepoLoader loader) {
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstalledModulesReloaded(ModuleUtil moduleUtil) {
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModuleEnableChange(ModuleUtil moduleUtil) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, ModuleUtil.InstalledModule module) {
|
||||
if (this.packageName.equals(packageName))
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_download_details, menu);
|
||||
|
||||
boolean updateIgnorePreference = App.getPreferences().getBoolean("ignore_updates", false);
|
||||
if (updateIgnorePreference) {
|
||||
SharedPreferences prefs = getSharedPreferences("update_ignored", MODE_PRIVATE);
|
||||
|
||||
boolean ignored = prefs.getBoolean(module.packageName, false);
|
||||
menu.findItem(R.id.ignoreUpdate).setChecked(ignored);
|
||||
} else {
|
||||
menu.removeItem(R.id.ignoreUpdate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.menu_refresh) {
|
||||
RepoLoader.getInstance().triggerReload(true);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_share) {
|
||||
String text = module.name + " - ";
|
||||
|
||||
if (isPackageInstalled(packageName, this)) {
|
||||
String s = getPackageManager().getInstallerPackageName(packageName);
|
||||
boolean playStore;
|
||||
|
||||
try {
|
||||
playStore = PLAY_STORE_PACKAGE.equals(s);
|
||||
} catch (NullPointerException e) {
|
||||
playStore = false;
|
||||
}
|
||||
|
||||
if (playStore) {
|
||||
text += String.format(PLAY_STORE_LINK, packageName);
|
||||
} else {
|
||||
text += String.format(XPOSED_REPO_LINK, packageName);
|
||||
}
|
||||
} else {
|
||||
text += String.format(XPOSED_REPO_LINK,
|
||||
packageName);
|
||||
}
|
||||
|
||||
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
|
||||
sharingIntent.setType("text/plain");
|
||||
sharingIntent.putExtra(Intent.EXTRA_TEXT, text);
|
||||
startActivity(Intent.createChooser(sharingIntent, getString(R.string.share)));
|
||||
return true;
|
||||
} else if (itemId == R.id.ignoreUpdate) {
|
||||
SharedPreferences prefs = getSharedPreferences("update_ignored", MODE_PRIVATE);
|
||||
|
||||
boolean ignored = prefs.getBoolean(module.packageName, false);
|
||||
prefs.edit().putBoolean(module.packageName, !ignored).apply();
|
||||
item.setChecked(!ignored);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private boolean isPackageInstalled(String packagename, Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class SwipeFragmentPagerAdapter extends FragmentPagerAdapter {
|
||||
final int PAGE_COUNT = 3;
|
||||
private final String[] tabTitles = new String[]{getString(R.string.download_details_page_description), getString(R.string.download_details_page_versions), getString(R.string.download_details_page_settings),};
|
||||
|
||||
SwipeFragmentPagerAdapter(FragmentManager fm) {
|
||||
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return PAGE_COUNT;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
case DOWNLOAD_DESCRIPTION:
|
||||
return new DownloadDetailsFragment();
|
||||
case DOWNLOAD_VERSIONS:
|
||||
return new DownloadDetailsVersionsFragment();
|
||||
case DOWNLOAD_SETTINGS:
|
||||
return new DownloadDetailsSettingsFragment();
|
||||
default:
|
||||
//noinspection ConstantConditions
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
// Generate title based on item position
|
||||
return tabTitles[position];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,8 @@ import org.meowcat.edxposed.manager.util.GlideHelper;
|
|||
import org.meowcat.edxposed.manager.util.ModuleUtil;
|
||||
import org.meowcat.edxposed.manager.util.light.Light;
|
||||
|
||||
public class MainActivity extends BaseActivity implements /*RepoLoader.RepoListener, */ModuleUtil.ModuleListener {
|
||||
public class MainActivity extends BaseActivity implements ModuleUtil.ModuleListener {
|
||||
ActivityMainBinding binding;
|
||||
//private RepoLoader repoLoader;
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
@Override
|
||||
|
|
@ -33,23 +32,15 @@ public class MainActivity extends BaseActivity implements /*RepoLoader.RepoListe
|
|||
if (Light.setLightSourceAlpha(getWindow().getDecorView(), 0.01f, 0.029f)) {
|
||||
binding.status.setElevation(24);
|
||||
binding.modules.setElevation(12);
|
||||
//binding.downloads.setElevation(12);
|
||||
}
|
||||
});
|
||||
setupWindowInsets(binding.snackbar, null);
|
||||
//repoLoader = RepoLoader.getInstance();
|
||||
ModuleUtil.getInstance().addListener(this);
|
||||
//repoLoader.addListener(this, false);
|
||||
binding.modules.setOnClickListener(v -> {
|
||||
Intent intent = new Intent();
|
||||
intent.setClass(getApplicationContext(), ModulesActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
/*binding.downloads.setOnClickListener(v -> {
|
||||
Intent intent = new Intent();
|
||||
intent.setClass(getApplicationContext(), DownloadActivity.class);
|
||||
startActivity(intent);
|
||||
});*/
|
||||
binding.apps.setOnClickListener(v -> {
|
||||
Intent intent = new Intent();
|
||||
intent.setClass(getApplicationContext(), BlackListActivity.class);
|
||||
|
|
@ -114,28 +105,9 @@ public class MainActivity extends BaseActivity implements /*RepoLoader.RepoListe
|
|||
new Thread(() -> new BlackListAdapter(getApplicationContext(), AppHelper.isWhiteListMode()).generateCheckedList());
|
||||
}
|
||||
|
||||
/*
|
||||
private void notifyDataSetChanged() {
|
||||
runOnUiThread(() -> {
|
||||
String frameworkUpdateVersion = repoLoader.getFrameworkUpdateVersion();
|
||||
boolean moduleUpdateAvailable = repoLoader.hasModuleUpdates();
|
||||
ModuleUtil.getInstance().getEnabledModules().size();
|
||||
binding.modulesSummary.setText(String.format(getString(R.string.ModulesDetail), ModuleUtil.getInstance().getEnabledModules().size()));
|
||||
if (frameworkUpdateVersion != null) {
|
||||
binding.statusSummary.setText(String.format(getString(R.string.welcome_framework_update_available), frameworkUpdateVersion));
|
||||
}
|
||||
if (moduleUpdateAvailable) {
|
||||
binding.downloadSummary.setText(R.string.modules_updates_available);
|
||||
} else {
|
||||
binding.downloadSummary.setText(R.string.ModuleUptodate);
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void onInstalledModulesReloaded(ModuleUtil moduleUtil) {
|
||||
//notifyDataSetChanged();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -145,19 +117,13 @@ public class MainActivity extends BaseActivity implements /*RepoLoader.RepoListe
|
|||
|
||||
@Override
|
||||
public void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, ModuleUtil.InstalledModule module) {
|
||||
//notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void onRepoReloaded(RepoLoader loader) {
|
||||
notifyDataSetChanged();
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
ModuleUtil.getInstance().removeListener(this);
|
||||
//repoLoader.removeListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ import org.meowcat.edxposed.manager.App;
|
|||
import org.meowcat.edxposed.manager.BuildConfig;
|
||||
import org.meowcat.edxposed.manager.Constants;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.adapters.AppAdapter;
|
||||
import org.meowcat.edxposed.manager.databinding.ActivityModulesBinding;
|
||||
import org.meowcat.edxposed.manager.util.GlideApp;
|
||||
import org.meowcat.edxposed.manager.util.InstallApkUtil;
|
||||
import org.meowcat.edxposed.manager.util.LinearLayoutManagerFix;
|
||||
import org.meowcat.edxposed.manager.util.ModuleUtil;
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
|
|||
showList = new ArrayList<>();
|
||||
String filter = queryStr.toLowerCase();
|
||||
for (ModuleUtil.InstalledModule info : fullList) {
|
||||
if (lowercaseContains(InstallApkUtil.getAppLabel(info.app, pm), filter)
|
||||
if (lowercaseContains(AppAdapter.getAppLabel(info.app, pm), filter)
|
||||
|| lowercaseContains(info.packageName, filter)) {
|
||||
showList.add(info);
|
||||
}
|
||||
|
|
@ -351,52 +351,6 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/*
|
||||
private void importModules(Uri uri) {
|
||||
RepoLoader repoLoader = RepoLoader.getInstance();
|
||||
List<Module> list = new ArrayList<>();
|
||||
|
||||
try {
|
||||
InputStream inputStream = getContentResolver().openInputStream(uri);
|
||||
InputStreamReader isr = new InputStreamReader(inputStream);
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
Module m = repoLoader.getModule(line);
|
||||
|
||||
if (m == null) {
|
||||
Snackbar.make(binding.snackbar, getString(R.string.download_details_not_found, line), Snackbar.LENGTH_SHORT).show();
|
||||
} else {
|
||||
list.add(m);
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (final Module m : list) {
|
||||
if (moduleUtil.getModule(m.packageName) != null) {
|
||||
continue;
|
||||
}
|
||||
ModuleVersion mv = null;
|
||||
for (int i = 0; i < m.versions.size(); i++) {
|
||||
ModuleVersion mvTemp = m.versions.get(i);
|
||||
|
||||
if (mvTemp.relType == ReleaseType.STABLE) {
|
||||
mv = mvTemp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mv != null) {
|
||||
NavUtil.startURL(this, mv.downloadLink);
|
||||
}
|
||||
}
|
||||
|
||||
ModuleUtil.getInstance().reloadInstalledModules();
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
|
|
|||
|
|
@ -111,15 +111,7 @@ public class SettingsActivity extends BaseActivity {
|
|||
@Override
|
||||
public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.prefs);
|
||||
/*
|
||||
Preference releaseType = findPreference("release_type_global");
|
||||
if (releaseType != null) {
|
||||
releaseType.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
RepoLoader.getInstance().setReleaseTypeGlobal((String) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
SwitchPreferenceCompat prefWhiteListMode = findPreference("white_list_switch");
|
||||
if (prefWhiteListMode != null) {
|
||||
prefWhiteListMode.setChecked(Files.exists(whiteListModeFlag));
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.ui.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.databinding.DownloadDetailsBinding;
|
||||
import org.meowcat.edxposed.manager.databinding.DownloadMoreinfoBinding;
|
||||
import org.meowcat.edxposed.manager.repo.Module;
|
||||
import org.meowcat.edxposed.manager.repo.RepoParser;
|
||||
import org.meowcat.edxposed.manager.ui.activity.BaseActivity;
|
||||
import org.meowcat.edxposed.manager.ui.activity.DownloadDetailsActivity;
|
||||
import org.meowcat.edxposed.manager.util.NavUtil;
|
||||
import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod;
|
||||
|
||||
public class DownloadDetailsFragment extends Fragment {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
DownloadDetailsActivity mActivity = (DownloadDetailsActivity) getActivity();
|
||||
if (mActivity == null) {
|
||||
return null;
|
||||
}
|
||||
final Module module = mActivity.getModule();
|
||||
if (module == null) {
|
||||
return null;
|
||||
}
|
||||
DownloadDetailsBinding binding = DownloadDetailsBinding.inflate(inflater, container, false);
|
||||
binding.downloadTitle.setText(module.name);
|
||||
binding.downloadTitle.setTextIsSelectable(true);
|
||||
|
||||
if (module.author != null && !module.author.isEmpty())
|
||||
binding.downloadAuthor.setText(getString(R.string.download_author, module.author));
|
||||
else
|
||||
binding.downloadAuthor.setText(R.string.download_unknown_author);
|
||||
|
||||
if (module.description != null) {
|
||||
if (module.descriptionIsHtml) {
|
||||
binding.downloadDescription.setText(RepoParser.parseSimpleHtml(getActivity(), module.description, binding.downloadDescription));
|
||||
binding.downloadDescription.setTransformationMethod(new LinkTransformationMethod((BaseActivity) getActivity()));
|
||||
binding.downloadDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
} else {
|
||||
binding.downloadDescription.setText(module.description);
|
||||
}
|
||||
binding.downloadDescription.setTextIsSelectable(true);
|
||||
} else {
|
||||
binding.downloadDescription.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
for (Pair<String, String> moreInfoEntry : module.moreInfo) {
|
||||
DownloadMoreinfoBinding moreinfoBinding = DownloadMoreinfoBinding.inflate(inflater, binding.downloadMoreinfoContainer, false);
|
||||
|
||||
moreinfoBinding.title.setText(moreInfoEntry.first + ":");
|
||||
moreinfoBinding.message.setText(moreInfoEntry.second);
|
||||
|
||||
final Uri link = NavUtil.parseURL(moreInfoEntry.second);
|
||||
if (link != null) {
|
||||
moreinfoBinding.message.setTextColor(moreinfoBinding.message.getLinkTextColors());
|
||||
moreinfoBinding.getRoot().setOnClickListener(v -> NavUtil.startURL((BaseActivity) getActivity(), link));
|
||||
}
|
||||
|
||||
binding.downloadMoreinfoContainer.addView(moreinfoBinding.getRoot());
|
||||
}
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.ui.fragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.takisoft.preferencex.PreferenceFragmentCompat;
|
||||
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.repo.Module;
|
||||
import org.meowcat.edxposed.manager.ui.activity.DownloadDetailsActivity;
|
||||
import org.meowcat.edxposed.manager.util.PrefixedSharedPreferences;
|
||||
import org.meowcat.edxposed.manager.util.RepoLoader;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class DownloadDetailsSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) {
|
||||
DownloadDetailsActivity mActivity = (DownloadDetailsActivity) getActivity();
|
||||
if (mActivity == null) {
|
||||
return;
|
||||
}
|
||||
final Module module = mActivity.getModule();
|
||||
if (module == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String packageName = module.packageName;
|
||||
|
||||
PreferenceManager prefManager = getPreferenceManager();
|
||||
prefManager.setSharedPreferencesName("module_settings");
|
||||
PrefixedSharedPreferences.injectToPreferenceManager(prefManager, module.packageName);
|
||||
addPreferencesFromResource(R.xml.module_prefs);
|
||||
|
||||
SharedPreferences prefs = getActivity().getSharedPreferences("module_settings", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
if (prefs.getBoolean("no_global", true)) {
|
||||
for (Map.Entry<String, ?> k : prefs.getAll().entrySet()) {
|
||||
if (("global").equals(prefs.getString(k.getKey(), ""))) {
|
||||
editor.putString(k.getKey(), "").apply();
|
||||
}
|
||||
}
|
||||
|
||||
editor.putBoolean("no_global", false).apply();
|
||||
}
|
||||
|
||||
Preference releaseType = findPreference("release_type");
|
||||
if (releaseType != null) {
|
||||
releaseType.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
RepoLoader.getInstance().setReleaseTypeLocal(packageName, (String) newValue);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.ui.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.repo.Module;
|
||||
import org.meowcat.edxposed.manager.repo.ModuleVersion;
|
||||
import org.meowcat.edxposed.manager.repo.ReleaseType;
|
||||
import org.meowcat.edxposed.manager.repo.RepoParser;
|
||||
import org.meowcat.edxposed.manager.ui.activity.BaseActivity;
|
||||
import org.meowcat.edxposed.manager.ui.activity.DownloadDetailsActivity;
|
||||
import org.meowcat.edxposed.manager.ui.widget.DownloadView;
|
||||
import org.meowcat.edxposed.manager.util.ModuleUtil.InstalledModule;
|
||||
import org.meowcat.edxposed.manager.util.RepoLoader;
|
||||
import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class DownloadDetailsVersionsFragment extends ListFragment {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private DownloadDetailsActivity activity;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
activity = (DownloadDetailsActivity) getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
Module module = activity.getModule();
|
||||
if (module == null)
|
||||
return;
|
||||
|
||||
if (module.versions.isEmpty()) {
|
||||
setEmptyText(getString(R.string.download_no_versions));
|
||||
setListShown(true);
|
||||
} else {
|
||||
RepoLoader repoLoader = RepoLoader.getInstance();
|
||||
if (!repoLoader.isVersionShown(module.versions.get(0))) {
|
||||
TextView txtHeader = new TextView(getActivity());
|
||||
txtHeader.setText(R.string.download_test_version_not_shown);
|
||||
txtHeader.setTextColor(ContextCompat.getColor(activity, R.color.warning));
|
||||
txtHeader.setOnClickListener(v -> activity.gotoPage(DownloadDetailsActivity.DOWNLOAD_SETTINGS));
|
||||
getListView().addHeaderView(txtHeader);
|
||||
}
|
||||
|
||||
VersionsAdapter sAdapter = new VersionsAdapter(activity, activity.getInstalledModule()/*, activity.findViewById(R.id.snackbar)*/);
|
||||
for (ModuleVersion version : module.versions) {
|
||||
if (repoLoader.isVersionShown(version))
|
||||
sAdapter.add(version);
|
||||
}
|
||||
setListAdapter(sAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
setListAdapter(null);
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
TextView txtStatus;
|
||||
TextView txtVersion;
|
||||
TextView txtRelType;
|
||||
TextView txtUploadDate;
|
||||
DownloadView downloadView;
|
||||
TextView txtChangesTitle;
|
||||
TextView txtChanges;
|
||||
}
|
||||
|
||||
private class VersionsAdapter extends ArrayAdapter<ModuleVersion> {
|
||||
private final DateFormat dateFormatter = DateFormat
|
||||
.getDateInstance(DateFormat.SHORT);
|
||||
private final int colorRelTypeStable;
|
||||
private final int colorRelTypeOthers;
|
||||
private final int colorInstalled;
|
||||
private final int colorUpdateAvailable;
|
||||
private final String textInstalled;
|
||||
private final String textUpdateAvailable;
|
||||
private final long installedVersionCode;
|
||||
|
||||
VersionsAdapter(Context context, InstalledModule installed) {
|
||||
super(context, R.layout.item_version);
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
theme.resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
|
||||
int color = ContextCompat.getColor(context, typedValue.resourceId);
|
||||
colorRelTypeStable = color;
|
||||
colorRelTypeOthers = ContextCompat.getColor(activity, R.color.warning);
|
||||
colorInstalled = color;
|
||||
colorUpdateAvailable = ContextCompat.getColor(activity, R.color.download_status_update_available);
|
||||
textInstalled = getString(R.string.download_section_installed) + ":";
|
||||
textUpdateAvailable = getString(R.string.download_section_update_available) + ":";
|
||||
installedVersionCode = (installed != null) ? installed.versionCode : -1;
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@Override
|
||||
@NonNull
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
View view = convertView;
|
||||
if (view == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = inflater.inflate(R.layout.item_version, null, true);
|
||||
ViewHolder viewHolder = new ViewHolder();
|
||||
viewHolder.txtStatus = view.findViewById(R.id.txtStatus);
|
||||
viewHolder.txtVersion = view.findViewById(R.id.txtVersion);
|
||||
viewHolder.txtRelType = view.findViewById(R.id.txtRelType);
|
||||
viewHolder.txtUploadDate = view.findViewById(R.id.txtUploadDate);
|
||||
viewHolder.downloadView = view.findViewById(R.id.downloadView);
|
||||
viewHolder.txtChangesTitle = view.findViewById(R.id.txtChangesTitle);
|
||||
viewHolder.txtChanges = view.findViewById(R.id.txtChanges);
|
||||
viewHolder.downloadView.fragment = DownloadDetailsVersionsFragment.this;
|
||||
view.setTag(viewHolder);
|
||||
}
|
||||
|
||||
ViewHolder holder = (ViewHolder) view.getTag();
|
||||
ModuleVersion item = getItem(position);
|
||||
if (item == null) {
|
||||
return view;
|
||||
}
|
||||
holder.txtVersion.setText(item.name);
|
||||
holder.txtRelType.setText(item.relType.getTitleId());
|
||||
holder.txtRelType.setTextColor(item.relType == ReleaseType.STABLE
|
||||
? colorRelTypeStable : colorRelTypeOthers);
|
||||
|
||||
if (item.uploaded > 0) {
|
||||
holder.txtUploadDate.setText(
|
||||
dateFormatter.format(new Date(item.uploaded)));
|
||||
holder.txtUploadDate.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.txtUploadDate.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (item.code <= 0 || installedVersionCode <= 0
|
||||
|| item.code < installedVersionCode) {
|
||||
holder.txtStatus.setVisibility(View.GONE);
|
||||
} else if (item.code == installedVersionCode) {
|
||||
holder.txtStatus.setText(textInstalled);
|
||||
holder.txtStatus.setTextColor(colorInstalled);
|
||||
holder.txtStatus.setVisibility(View.VISIBLE);
|
||||
} else { // item.code > installedVersionCode
|
||||
holder.txtStatus.setText(textUpdateAvailable);
|
||||
holder.txtStatus.setTextColor(colorUpdateAvailable);
|
||||
holder.txtStatus.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
holder.downloadView.setUrl(item.downloadLink);
|
||||
holder.downloadView.setTitle(activity.getModule().name);
|
||||
|
||||
if (item.changelog != null && !item.changelog.isEmpty()) {
|
||||
holder.txtChangesTitle.setVisibility(View.VISIBLE);
|
||||
holder.txtChanges.setVisibility(View.VISIBLE);
|
||||
|
||||
if (item.changelogIsHtml) {
|
||||
holder.txtChanges.setText(RepoParser.parseSimpleHtml(getActivity(), item.changelog, holder.txtChanges));
|
||||
holder.txtChanges.setTransformationMethod(new LinkTransformationMethod((BaseActivity) getActivity()));
|
||||
holder.txtChanges.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
} else {
|
||||
holder.txtChanges.setText(item.changelog);
|
||||
holder.txtChanges.setMovementMethod(null);
|
||||
}
|
||||
|
||||
} else {
|
||||
holder.txtChangesTitle.setVisibility(View.GONE);
|
||||
holder.txtChanges.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.databinding.DownloadViewBinding;
|
||||
import org.meowcat.edxposed.manager.ui.activity.BaseActivity;
|
||||
import org.meowcat.edxposed.manager.util.NavUtil;
|
||||
|
||||
public class DownloadView extends LinearLayout {
|
||||
public Fragment fragment;
|
||||
private String mUrl = null;
|
||||
private String mTitle = null;
|
||||
private final DownloadViewBinding binding;
|
||||
|
||||
public DownloadView(Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setFocusable(false);
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
binding = DownloadViewBinding.inflate(LayoutInflater.from(context), this);
|
||||
|
||||
binding.btnDownload.setOnClickListener(v -> NavUtil.startURL((BaseActivity) context, mUrl));
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
mUrl = url;
|
||||
if (mUrl != null) {
|
||||
binding.btnDownload.setVisibility(View.VISIBLE);
|
||||
binding.txtInfo.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.btnDownload.setVisibility(View.GONE);
|
||||
binding.txtInfo.setVisibility(View.VISIBLE);
|
||||
binding.txtInfo.setText(R.string.download_view_no_url);
|
||||
}
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.mTitle = title;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.meowcat.edxposed.manager.App;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
public class DownloadsUtil {
|
||||
private static final SharedPreferences pref = App.getInstance().getSharedPreferences("download_cache", Context.MODE_PRIVATE);
|
||||
|
||||
static SyncDownloadInfo downloadSynchronously(String url, File target) {
|
||||
final boolean useNotModifiedTags = target.exists();
|
||||
|
||||
URLConnection connection = null;
|
||||
InputStream in = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
connection = new URL(url).openConnection();
|
||||
connection.setDoOutput(false);
|
||||
connection.setConnectTimeout(30000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
// Disable transparent gzip encoding for gzipped files
|
||||
if (url.endsWith(".gz")) {
|
||||
connection.addRequestProperty("Accept-Encoding", "identity");
|
||||
}
|
||||
|
||||
if (useNotModifiedTags) {
|
||||
String modified = pref.getString("download_" + url + "_modified", null);
|
||||
String etag = pref.getString("download_" + url + "_etag", null);
|
||||
|
||||
if (modified != null) {
|
||||
connection.addRequestProperty("If-Modified-Since", modified);
|
||||
}
|
||||
if (etag != null) {
|
||||
connection.addRequestProperty("If-None-Match", etag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
HttpURLConnection httpConnection = (HttpURLConnection) connection;
|
||||
int responseCode = httpConnection.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||
return new SyncDownloadInfo(SyncDownloadInfo.STATUS_NOT_MODIFIED, null);
|
||||
} else if (responseCode < 200 || responseCode >= 300) {
|
||||
return new SyncDownloadInfo(SyncDownloadInfo.STATUS_FAILED,
|
||||
App.getInstance().getString(R.string.repo_download_failed_http,
|
||||
url, responseCode,
|
||||
httpConnection.getResponseMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
in = connection.getInputStream();
|
||||
out = new FileOutputStream(target);
|
||||
byte[] buf = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, read);
|
||||
}
|
||||
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
HttpURLConnection httpConnection = (HttpURLConnection) connection;
|
||||
String modified = httpConnection.getHeaderField("Last-Modified");
|
||||
String etag = httpConnection.getHeaderField("ETag");
|
||||
|
||||
pref.edit()
|
||||
.putString("download_" + url + "_modified", modified)
|
||||
.putString("download_" + url + "_etag", etag).apply();
|
||||
}
|
||||
|
||||
return new SyncDownloadInfo(SyncDownloadInfo.STATUS_SUCCESS, null);
|
||||
|
||||
} catch (Throwable t) {
|
||||
return new SyncDownloadInfo(SyncDownloadInfo.STATUS_FAILED,
|
||||
App.getInstance().getString(R.string.repo_download_failed, url,
|
||||
t.getMessage()));
|
||||
|
||||
} finally {
|
||||
if (connection instanceof HttpURLConnection)
|
||||
((HttpURLConnection) connection).disconnect();
|
||||
if (in != null)
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
if (out != null)
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clearCache(String url) {
|
||||
if (url != null) {
|
||||
pref.edit().remove("download_" + url + "_modified")
|
||||
.remove("download_" + url + "_etag").apply();
|
||||
} else {
|
||||
pref.edit().clear().apply();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SyncDownloadInfo {
|
||||
static final int STATUS_SUCCESS = 0;
|
||||
static final int STATUS_NOT_MODIFIED = 1;
|
||||
static final int STATUS_FAILED = 2;
|
||||
|
||||
public final int status;
|
||||
final String errorMessage;
|
||||
|
||||
private SyncDownloadInfo(int status, String errorMessage) {
|
||||
this.status = status;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class HashUtil {
|
||||
private static String hash(String input, @SuppressWarnings("SameParameterValue") String algorithm) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
||||
byte[] messageDigest = md.digest(input.getBytes());
|
||||
return toHexString(messageDigest);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static String md5(String input) {
|
||||
return hash(input, "MD5");
|
||||
}
|
||||
|
||||
// public static String sha1(String input) {
|
||||
// return hash(input, "SHA-1");
|
||||
// }
|
||||
|
||||
private static String hash(File file, @SuppressWarnings("SameParameterValue") String algorithm) throws IOException {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
||||
InputStream is = new FileInputStream(file);
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while ((read = is.read(buffer)) > 0) {
|
||||
md.update(buffer, 0, read);
|
||||
}
|
||||
is.close();
|
||||
byte[] messageDigest = md.digest();
|
||||
return toHexString(messageDigest);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String md5(File input) throws IOException {
|
||||
return hash(input, "MD5");
|
||||
}
|
||||
|
||||
// public static String sha1(File input) throws IOException {
|
||||
// return hash(input, "SHA-1");
|
||||
// }
|
||||
|
||||
private static String toHexString(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
int unsignedB = b & 0xff;
|
||||
if (unsignedB < 0x10)
|
||||
sb.append("0");
|
||||
sb.append(Integer.toHexString(unsignedB));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.util;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
|
||||
public class InstallApkUtil {
|
||||
|
||||
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
|
||||
try {
|
||||
if (info.labelRes > 0) {
|
||||
Resources res = pm.getResourcesForApplication(info);
|
||||
return res.getString(info.labelRes);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return info.loadLabel(pm).toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ import org.meowcat.edxposed.manager.App;
|
|||
import org.meowcat.edxposed.manager.Constants;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.databinding.ActivityModulesBinding;
|
||||
import org.meowcat.edxposed.manager.repo.ModuleVersion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
|
@ -33,7 +32,6 @@ public final class ModuleUtil {
|
|||
public static int MIN_MODULE_VERSION = 2; // reject modules with
|
||||
private static ModuleUtil instance = null;
|
||||
private final PackageManager pm;
|
||||
private final String frameworkPackageName;
|
||||
private final List<ModuleListener> listeners = new CopyOnWriteArrayList<>();
|
||||
private final SharedPreferences pref;
|
||||
//private InstalledModule framework = null;
|
||||
|
|
@ -44,7 +42,6 @@ public final class ModuleUtil {
|
|||
private ModuleUtil() {
|
||||
pref = App.getInstance().getSharedPreferences("enabled_modules", Context.MODE_PRIVATE);
|
||||
pm = App.getInstance().getPackageManager();
|
||||
frameworkPackageName = App.getInstance().getPackageName();
|
||||
}
|
||||
|
||||
public static synchronized ModuleUtil getInstance() {
|
||||
|
|
@ -76,32 +73,19 @@ public final class ModuleUtil {
|
|||
}
|
||||
|
||||
Map<String, InstalledModule> modules = new HashMap<>();
|
||||
//RepoDb.beginTransation();
|
||||
try {
|
||||
//RepoDb.deleteAllInstalledModules();
|
||||
|
||||
for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_META_DATA)) {
|
||||
ApplicationInfo app = pkg.applicationInfo;
|
||||
if (!app.enabled)
|
||||
continue;
|
||||
for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_META_DATA)) {
|
||||
ApplicationInfo app = pkg.applicationInfo;
|
||||
if (!app.enabled)
|
||||
continue;
|
||||
|
||||
InstalledModule installed = null;
|
||||
if (app.metaData != null && app.metaData.containsKey("xposedmodule")) {
|
||||
installed = new InstalledModule(pkg, false);
|
||||
modules.put(pkg.packageName, installed);
|
||||
}/* else if (isFramework(pkg.packageName)) {
|
||||
framework = installed = new InstalledModule(pkg, true);
|
||||
}*/
|
||||
|
||||
//if (installed != null)
|
||||
// RepoDb.insertInstalledModule(installed);
|
||||
if (app.metaData != null && app.metaData.containsKey("xposedmodule")) {
|
||||
InstalledModule installed = new InstalledModule(pkg, false);
|
||||
modules.put(pkg.packageName, installed);
|
||||
}
|
||||
|
||||
//RepoDb.setTransactionSuccessful();
|
||||
} finally {
|
||||
//RepoDb.endTransation();
|
||||
}
|
||||
|
||||
|
||||
installedModules = modules;
|
||||
synchronized (this) {
|
||||
isReloading = false;
|
||||
|
|
@ -129,7 +113,6 @@ public final class ModuleUtil {
|
|||
ApplicationInfo app = pkg.applicationInfo;
|
||||
if (app.enabled && app.metaData != null && app.metaData.containsKey("xposedmodule")) {
|
||||
InstalledModule module = new InstalledModule(pkg, false);
|
||||
//RepoDb.insertInstalledModule(module);
|
||||
installedModules.put(packageName, module);
|
||||
for (ModuleListener listener : listeners) {
|
||||
listener.onSingleInstalledModuleReloaded(instance, packageName,
|
||||
|
|
@ -137,7 +120,6 @@ public final class ModuleUtil {
|
|||
}
|
||||
return module;
|
||||
} else {
|
||||
//RepoDb.deleteInstalledModule(packageName);
|
||||
InstalledModule old = installedModules.remove(packageName);
|
||||
if (old != null) {
|
||||
for (ModuleListener listener : listeners) {
|
||||
|
|
@ -148,26 +130,6 @@ public final class ModuleUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isLoading() {
|
||||
return isReloading;
|
||||
}
|
||||
|
||||
/* public InstalledModule getFramework() {
|
||||
return framework;
|
||||
}*/
|
||||
|
||||
public String getFrameworkPackageName() {
|
||||
return frameworkPackageName;
|
||||
}
|
||||
|
||||
/* private boolean isFramework(String packageName) {
|
||||
return frameworkPackageName.equals(packageName);
|
||||
}*/
|
||||
|
||||
// public boolean isInstalled(String packageName) {
|
||||
// return installedModules.containsKey(packageName) || isFramework(packageName);
|
||||
// }
|
||||
|
||||
public InstalledModule getModule(String packageName) {
|
||||
return installedModules.get(packageName);
|
||||
}
|
||||
|
|
@ -370,10 +332,6 @@ public final class ModuleUtil {
|
|||
return this.description;
|
||||
}
|
||||
|
||||
public boolean isUpdate(ModuleVersion version) {
|
||||
return (version != null) && version.code > versionCode;
|
||||
}
|
||||
|
||||
public PackageInfo getPackageInfo() {
|
||||
return pkg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ public final class NotificationUtil {
|
|||
|
||||
public static final int NOTIFICATION_MODULE_NOT_ACTIVATED_YET = 0;
|
||||
private static final int NOTIFICATION_MODULES_UPDATED = 1;
|
||||
private static final int NOTIFICATION_INSTALLER_UPDATE = 2;
|
||||
private static final int PENDING_INTENT_OPEN_MODULES = 0;
|
||||
private static final int PENDING_INTENT_OPEN_INSTALL = 1;
|
||||
private static final int PENDING_INTENT_SOFT_REBOOT = 2;
|
||||
|
|
@ -36,7 +35,6 @@ public final class NotificationUtil {
|
|||
private static final String HEADS_UP = "heads_up";
|
||||
private static final String FRAGMENT_ID = "fragment";
|
||||
|
||||
private static final String NOTIFICATION_UPDATE_CHANNEL = "app_update_channel";
|
||||
private static final String NOTIFICATION_MODULES_CHANNEL = "modules_channel";
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
|
@ -54,9 +52,7 @@ public final class NotificationUtil {
|
|||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channelUpdate = new NotificationChannel(NOTIFICATION_UPDATE_CHANNEL, context.getString(R.string.download_section_update_available), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
NotificationChannel channelModule = new NotificationChannel(NOTIFICATION_MODULES_CHANNEL, context.getString(R.string.nav_item_modules), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
notificationManager.createNotificationChannel(channelUpdate);
|
||||
notificationManager.createNotificationChannel(channelModule);
|
||||
}
|
||||
}
|
||||
|
|
@ -137,30 +133,6 @@ public final class NotificationUtil {
|
|||
notificationManager.notify(null, NOTIFICATION_MODULES_UPDATED, builder.build());
|
||||
}
|
||||
|
||||
public static void showInstallerUpdateNotification() {
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(FRAGMENT_ID, 0);
|
||||
|
||||
PendingIntent pInstallTab = PendingIntent.getActivity(context, PENDING_INTENT_OPEN_INSTALL,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
String title = context.getString(R.string.app_name);
|
||||
String message = context.getString(R.string.newVersion);
|
||||
NotificationCompat.Builder builder = getNotificationBuilder(title, message, NOTIFICATION_UPDATE_CHANNEL)
|
||||
.setContentIntent(pInstallTab);
|
||||
|
||||
if (prefs.getBoolean(HEADS_UP, true)) {
|
||||
builder.setPriority(2);
|
||||
}
|
||||
NotificationCompat.BigTextStyle notiStyle = new NotificationCompat.BigTextStyle();
|
||||
notiStyle.setBigContentTitle(title);
|
||||
notiStyle.bigText(message);
|
||||
builder.setStyle(notiStyle);
|
||||
|
||||
notificationManager.notify(null, NOTIFICATION_INSTALLER_UPDATE, builder.build());
|
||||
}
|
||||
|
||||
private static NotificationCompat.Builder getNotificationBuilder(String title, String message, String channel) {
|
||||
return new NotificationCompat.Builder(context, channel)
|
||||
.setContentTitle(title)
|
||||
|
|
|
|||
|
|
@ -1,161 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
public class PrefixedSharedPreferences implements SharedPreferences {
|
||||
private final SharedPreferences mBase;
|
||||
private final String mPrefix;
|
||||
|
||||
private PrefixedSharedPreferences(SharedPreferences base, String prefix) {
|
||||
mBase = base;
|
||||
mPrefix = prefix + "_";
|
||||
}
|
||||
|
||||
public static void injectToPreferenceManager(PreferenceManager manager, String prefix) {
|
||||
SharedPreferences prefixedPrefs = new PrefixedSharedPreferences(manager.getSharedPreferences(), prefix);
|
||||
|
||||
try {
|
||||
Field fieldSharedPref = PreferenceManager.class.getDeclaredField("mSharedPreferences");
|
||||
fieldSharedPref.setAccessible(true);
|
||||
fieldSharedPref.set(manager, prefixedPrefs);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> getAll() {
|
||||
Map<String, ?> baseResult = mBase.getAll();
|
||||
Map<String, Object> prefixedResult = new HashMap<>(baseResult);
|
||||
for (Entry<String, ?> entry : baseResult.entrySet()) {
|
||||
prefixedResult.put(mPrefix + entry.getKey(), entry.getValue());
|
||||
}
|
||||
return prefixedResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String defValue) {
|
||||
return mBase.getString(mPrefix + key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStringSet(String key, Set<String> defValues) {
|
||||
return mBase.getStringSet(mPrefix + key, defValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defValue) {
|
||||
return mBase.getInt(mPrefix + key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defValue) {
|
||||
return mBase.getLong(mPrefix + key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(String key, float defValue) {
|
||||
return mBase.getFloat(mPrefix + key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defValue) {
|
||||
return mBase.getBoolean(mPrefix + key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return mBase.contains(mPrefix + key);
|
||||
}
|
||||
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
@Override
|
||||
public Editor edit() {
|
||||
return new EditorImpl(mBase.edit());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||
throw new UnsupportedOperationException("listeners are not supported in this implementation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||
throw new UnsupportedOperationException("listeners are not supported in this implementation");
|
||||
}
|
||||
|
||||
private class EditorImpl implements Editor {
|
||||
private final Editor mEditorBase;
|
||||
|
||||
public EditorImpl(Editor base) {
|
||||
mEditorBase = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putString(String key, String value) {
|
||||
mEditorBase.putString(mPrefix + key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putStringSet(String key, Set<String> values) {
|
||||
mEditorBase.putStringSet(mPrefix + key, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putInt(String key, int value) {
|
||||
mEditorBase.putInt(mPrefix + key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putLong(String key, long value) {
|
||||
mEditorBase.putLong(mPrefix + key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putFloat(String key, float value) {
|
||||
mEditorBase.putFloat(mPrefix + key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putBoolean(String key, boolean value) {
|
||||
mEditorBase.putBoolean(mPrefix + key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor remove(String key) {
|
||||
mEditorBase.remove(mPrefix + key);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor clear() {
|
||||
mEditorBase.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commit() {
|
||||
return mEditorBase.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
mEditorBase.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,434 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.util;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.meowcat.edxposed.manager.App;
|
||||
import org.meowcat.edxposed.manager.R;
|
||||
import org.meowcat.edxposed.manager.repo.Module;
|
||||
import org.meowcat.edxposed.manager.repo.ModuleVersion;
|
||||
import org.meowcat.edxposed.manager.repo.ReleaseType;
|
||||
import org.meowcat.edxposed.manager.repo.RepoDb;
|
||||
import org.meowcat.edxposed.manager.repo.RepoParser;
|
||||
import org.meowcat.edxposed.manager.repo.RepoParser.RepoParserCallback;
|
||||
import org.meowcat.edxposed.manager.repo.Repository;
|
||||
import org.meowcat.edxposed.manager.ui.activity.DownloadActivity;
|
||||
import org.meowcat.edxposed.manager.util.DownloadsUtil.SyncDownloadInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class RepoLoader {
|
||||
private static final int UPDATE_FREQUENCY = 24 * 60 * 60 * 1000;
|
||||
private static String DEFAULT_REPOSITORIES;
|
||||
private static RepoLoader instance = null;
|
||||
private final List<RepoListener> listeners = new CopyOnWriteArrayList<>();
|
||||
private final Map<String, ReleaseType> localReleaseTypesCache = new HashMap<>();
|
||||
private final App app;
|
||||
private final SharedPreferences pref;
|
||||
private final SharedPreferences modulePref;
|
||||
private final ConnectivityManager conMgr;
|
||||
private boolean isLoading = false;
|
||||
private boolean reloadTriggeredOnce = false;
|
||||
private Map<Long, Repository> repositories = null;
|
||||
private ReleaseType globalReleaseType;
|
||||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
|
||||
private RepoLoader() {
|
||||
instance = this;
|
||||
app = App.getInstance();
|
||||
pref = app.getSharedPreferences("repo", Context.MODE_PRIVATE);
|
||||
DEFAULT_REPOSITORIES = App.getPreferences().getBoolean("custom_list", false) ? "https://cdn.jsdelivr.net/gh/ElderDrivers/Repository-Website@gh-pages/assets/full.xml.gz" : "https://dl-xda.xposed.info/repo/full.xml.gz";
|
||||
modulePref = app.getSharedPreferences("module_settings", Context.MODE_PRIVATE);
|
||||
conMgr = (ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
globalReleaseType = ReleaseType.fromString(App.getPreferences().getString("release_type_global", "stable"));
|
||||
refreshRepositories();
|
||||
}
|
||||
|
||||
public static synchronized RepoLoader getInstance() {
|
||||
if (instance == null)
|
||||
new RepoLoader();
|
||||
return instance;
|
||||
}
|
||||
|
||||
private boolean refreshRepositories() {
|
||||
repositories = RepoDb.getRepositories();
|
||||
|
||||
// Unlikely case (usually only during initial load): DB state doesn't
|
||||
// fit to configuration
|
||||
boolean needReload = false;
|
||||
String[] config = (pref.getString("repositories", DEFAULT_REPOSITORIES) + "").split("\\|");
|
||||
if (repositories.size() != config.length) {
|
||||
needReload = true;
|
||||
} else {
|
||||
int i = 0;
|
||||
for (Repository repo : repositories.values()) {
|
||||
if (!repo.url.equals(config[i++])) {
|
||||
needReload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!needReload)
|
||||
return false;
|
||||
|
||||
clear(false);
|
||||
for (String url : config) {
|
||||
RepoDb.insertRepository(url);
|
||||
}
|
||||
repositories = RepoDb.getRepositories();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setReleaseTypeGlobal(String relTypeString) {
|
||||
ReleaseType relType = ReleaseType.fromString(relTypeString);
|
||||
if (globalReleaseType == relType)
|
||||
return;
|
||||
|
||||
globalReleaseType = relType;
|
||||
|
||||
// Updating the latest version for all modules takes a moment
|
||||
new Thread("DBUpdate") {
|
||||
@Override
|
||||
public void run() {
|
||||
RepoDb.updateAllModulesLatestVersion();
|
||||
notifyListeners();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void setReleaseTypeLocal(String packageName, String relTypeString) {
|
||||
ReleaseType relType = (!TextUtils.isEmpty(relTypeString)) ? ReleaseType.fromString(relTypeString) : null;
|
||||
|
||||
if (getReleaseTypeLocal(packageName) == relType)
|
||||
return;
|
||||
|
||||
synchronized (localReleaseTypesCache) {
|
||||
if (relType != null) {
|
||||
localReleaseTypesCache.put(packageName, relType);
|
||||
}
|
||||
}
|
||||
|
||||
RepoDb.updateModuleLatestVersion(packageName);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
private ReleaseType getReleaseTypeLocal(String packageName) {
|
||||
synchronized (localReleaseTypesCache) {
|
||||
if (localReleaseTypesCache.containsKey(packageName))
|
||||
return localReleaseTypesCache.get(packageName);
|
||||
|
||||
String value = modulePref.getString(packageName + "_release_type",
|
||||
null);
|
||||
ReleaseType result = (!TextUtils.isEmpty(value)) ? ReleaseType.fromString(value) : null;
|
||||
if (result != null) {
|
||||
localReleaseTypesCache.put(packageName, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Repository getRepository(long repoId) {
|
||||
return repositories.get(repoId);
|
||||
}
|
||||
|
||||
public Module getModule(String packageName) {
|
||||
return RepoDb.getModuleByPackageName(packageName);
|
||||
}
|
||||
|
||||
public ModuleVersion getLatestVersion(Module module) {
|
||||
if (module == null || module.versions.isEmpty())
|
||||
return null;
|
||||
|
||||
for (ModuleVersion version : module.versions) {
|
||||
if (version.downloadLink != null && isVersionShown(version))
|
||||
return version;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isVersionShown(ModuleVersion version) {
|
||||
return version.relType
|
||||
.ordinal() <= getMaxShownReleaseType(version.module.packageName).ordinal();
|
||||
}
|
||||
|
||||
public ReleaseType getMaxShownReleaseType(String packageName) {
|
||||
ReleaseType localSetting = getReleaseTypeLocal(packageName);
|
||||
if (localSetting != null)
|
||||
return localSetting;
|
||||
else
|
||||
return globalReleaseType;
|
||||
}
|
||||
|
||||
public void triggerReload(final boolean force) {
|
||||
reloadTriggeredOnce = true;
|
||||
|
||||
if (force) {
|
||||
resetLastUpdateCheck();
|
||||
} else {
|
||||
long lastUpdateCheck = pref.getLong("last_update_check", 0);
|
||||
if (System.currentTimeMillis() < lastUpdateCheck + UPDATE_FREQUENCY)
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkInfo netInfo = conMgr.getActiveNetworkInfo();
|
||||
if (netInfo == null || !netInfo.isConnected())
|
||||
return;
|
||||
|
||||
synchronized (this) {
|
||||
if (isLoading)
|
||||
return;
|
||||
isLoading = true;
|
||||
}
|
||||
//app.updateProgressIndicator(swipeRefreshLayout);
|
||||
|
||||
new Thread("RepositoryReload") {
|
||||
public void run() {
|
||||
final List<String> messages = new LinkedList<>();
|
||||
boolean hasChanged = downloadAndParseFiles(messages);
|
||||
|
||||
pref.edit().putLong("last_update_check", System.currentTimeMillis()).apply();
|
||||
|
||||
if (!messages.isEmpty()) {
|
||||
App.runOnUiThread(() -> {
|
||||
for (String message : messages) {
|
||||
Toast.makeText(app, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (hasChanged)
|
||||
notifyListeners();
|
||||
|
||||
synchronized (this) {
|
||||
isLoading = false;
|
||||
}
|
||||
//app.updateProgressIndicator(swipeRefreshLayout);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void setSwipeRefreshLayout(SwipeRefreshLayout swipeRefreshLayout) {
|
||||
this.swipeRefreshLayout = swipeRefreshLayout;
|
||||
}
|
||||
|
||||
public void triggerFirstLoadIfNecessary() {
|
||||
if (!reloadTriggeredOnce)
|
||||
triggerReload(false);
|
||||
}
|
||||
|
||||
public void resetLastUpdateCheck() {
|
||||
pref.edit().remove("last_update_check").apply();
|
||||
}
|
||||
|
||||
public synchronized boolean isLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
|
||||
public void clear(boolean notify) {
|
||||
synchronized (this) {
|
||||
// TODO Stop reloading repository when it should be cleared
|
||||
if (isLoading)
|
||||
return;
|
||||
|
||||
RepoDb.deleteRepositories();
|
||||
repositories = new LinkedHashMap<>(0);
|
||||
DownloadsUtil.clearCache(null);
|
||||
resetLastUpdateCheck();
|
||||
}
|
||||
|
||||
if (notify)
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public void setRepositories(String... repos) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < repos.length; i++) {
|
||||
if (i > 0)
|
||||
sb.append("|");
|
||||
sb.append(repos[i]);
|
||||
}
|
||||
pref.edit().putString("repositories", sb.toString()).apply();
|
||||
if (refreshRepositories())
|
||||
triggerReload(true);
|
||||
}
|
||||
|
||||
public boolean hasModuleUpdates() {
|
||||
return RepoDb.hasModuleUpdates();
|
||||
}
|
||||
|
||||
public String getFrameworkUpdateVersion() {
|
||||
return RepoDb.getFrameworkUpdateVersion();
|
||||
}
|
||||
|
||||
private File getRepoCacheFile(String repo) {
|
||||
String filename = "repo_" + HashUtil.md5(repo) + ".xml";
|
||||
if (repo.endsWith(".gz"))
|
||||
filename += ".gz";
|
||||
return new File(app.getCacheDir(), filename);
|
||||
}
|
||||
|
||||
private boolean downloadAndParseFiles(List<String> messages) {
|
||||
// These variables don't need to be atomic, just mutable
|
||||
final AtomicBoolean hasChanged = new AtomicBoolean(false);
|
||||
final AtomicInteger insertCounter = new AtomicInteger();
|
||||
final AtomicInteger deleteCounter = new AtomicInteger();
|
||||
|
||||
for (Entry<Long, Repository> repoEntry : repositories.entrySet()) {
|
||||
final long repoId = repoEntry.getKey();
|
||||
final Repository repo = repoEntry.getValue();
|
||||
|
||||
String url = (repo.partialUrl != null && repo.version != null) ? String.format(repo.partialUrl, repo.version) : repo.url;
|
||||
|
||||
File cacheFile = getRepoCacheFile(url);
|
||||
SyncDownloadInfo info = DownloadsUtil.downloadSynchronously(url,
|
||||
cacheFile);
|
||||
|
||||
Log.i(App.TAG, String.format(
|
||||
"RepoLoader -> Downloaded %s with status %d (error: %s), size %d bytes",
|
||||
url, info.status, info.errorMessage, cacheFile.length()));
|
||||
|
||||
if (info.status != SyncDownloadInfo.STATUS_SUCCESS) {
|
||||
if (info.errorMessage != null)
|
||||
messages.add(info.errorMessage);
|
||||
continue;
|
||||
}
|
||||
|
||||
InputStream in = null;
|
||||
RepoDb.beginTransation();
|
||||
try {
|
||||
in = new FileInputStream(cacheFile);
|
||||
if (url.endsWith(".gz"))
|
||||
in = new GZIPInputStream(in);
|
||||
|
||||
RepoParser.parse(in, new RepoParserCallback() {
|
||||
@Override
|
||||
public void onRepositoryMetadata(Repository repository) {
|
||||
if (!repository.isPartial) {
|
||||
RepoDb.deleteAllModules(repoId);
|
||||
hasChanged.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewModule(Module module) {
|
||||
RepoDb.insertModule(repoId, module);
|
||||
hasChanged.set(true);
|
||||
insertCounter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveModule(String packageName) {
|
||||
RepoDb.deleteModule(repoId, packageName);
|
||||
hasChanged.set(true);
|
||||
deleteCounter.decrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted(Repository repository) {
|
||||
if (!repository.isPartial) {
|
||||
RepoDb.updateRepository(repoId, repository);
|
||||
repo.name = repository.name;
|
||||
repo.partialUrl = repository.partialUrl;
|
||||
repo.version = repository.version;
|
||||
} else {
|
||||
RepoDb.updateRepositoryVersion(repoId, repository.version);
|
||||
repo.version = repository.version;
|
||||
}
|
||||
|
||||
Log.i(App.TAG, String.format(
|
||||
"RepoLoader -> Updated repository %s to version %s (%d new / %d removed modules)",
|
||||
repo.url, repo.version, insertCounter.get(),
|
||||
deleteCounter.get()));
|
||||
}
|
||||
});
|
||||
|
||||
RepoDb.setTransactionSuccessful();
|
||||
} catch (SQLiteException e) {
|
||||
App.runOnUiThread(() -> new MaterialAlertDialogBuilder(app)
|
||||
.setTitle(R.string.restart_needed)
|
||||
.setMessage(R.string.cache_cleaned)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
Intent i = new Intent(app, DownloadActivity.class);
|
||||
PendingIntent pi = PendingIntent.getActivity(app, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
AlarmManager mgr = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE);
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pi);
|
||||
System.exit(0);
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show());
|
||||
|
||||
DownloadsUtil.clearCache(url);
|
||||
} catch (Throwable t) {
|
||||
Log.e(App.TAG, "RepoLoader -> Cannot load repository from " + url, t);
|
||||
messages.add(app.getString(R.string.repo_load_failed, url, t.getMessage()));
|
||||
DownloadsUtil.clearCache(url);
|
||||
} finally {
|
||||
if (in != null)
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
cacheFile.delete();
|
||||
RepoDb.endTransation();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Set ModuleColumns.PREFERRED for modules which appear in multiple
|
||||
// repositories
|
||||
return hasChanged.get();
|
||||
}
|
||||
|
||||
public void addListener(RepoListener listener, boolean triggerImmediately) {
|
||||
if (!listeners.contains(listener))
|
||||
listeners.add(listener);
|
||||
|
||||
if (triggerImmediately)
|
||||
listener.onRepoReloaded(this);
|
||||
}
|
||||
|
||||
public void removeListener(RepoListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private void notifyListeners() {
|
||||
for (RepoListener listener : listeners) {
|
||||
listener.onRepoReloaded(instance);
|
||||
}
|
||||
}
|
||||
|
||||
public interface RepoListener {
|
||||
/**
|
||||
* Called whenever the list of modules from repositories has been
|
||||
* successfully reloaded
|
||||
*/
|
||||
void onRepoReloaded(RepoLoader loader);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package org.meowcat.edxposed.manager.util;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class TaskRunner {
|
||||
private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
|
||||
|
||||
public <R> void executeAsync(Callable<R> callable) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
callable.call();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M13,5v6h1.17L12,13.17 9.83,11L11,11L11,5h2m2,-2L9,3v6L5,9l7,7 7,-7h-4L15,3zM19,18L5,18v2h14v-2z" />
|
||||
</vector>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92s2.92,-1.31 2.92,-2.92c0,-1.61 -1.31,-2.92 -2.92,-2.92zM18,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM6,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,20.02c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z" />
|
||||
</vector>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z" />
|
||||
</vector>
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/snackbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<org.meowcat.edxposed.manager.ui.widget.RecyclerViewBugFixed
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="?actionBarTheme"
|
||||
app:liftOnScrollTargetViewId="@id/recyclerView">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?colorActionBar"
|
||||
app:popupTheme="?actionBarPopupTheme" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/snackbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/download_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="?actionBarTheme"
|
||||
app:liftOnScroll="false">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?colorActionBar"
|
||||
app:popupTheme="?actionBarPopupTheme" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/sliding_tabs"
|
||||
style="?tabLayoutTheme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorActionBar"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
tools:context=".ui.activity.DownloadDetailsActivity">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/reload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menuReload" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -172,57 +172,6 @@
|
|||
</RelativeLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- <com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/downloads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="4dp"
|
||||
app:cardPreventCornerOverlap="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downloads_icon"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="@string/Downloads"
|
||||
app:srcCompat="@drawable/ic_get_app" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/downloads_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="25dp"
|
||||
android:layout_toEndOf="@id/downloads_icon"
|
||||
android:text="@string/Downloads"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/downloads_title"
|
||||
android:layout_alignStart="@id/downloads_title"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/ModuleUpgradable"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textColor="@android:color/darker_gray" />
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
-->
|
||||
<LinearLayout
|
||||
android:id="@+id/apps"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.fragment.DownloadDetailsFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_author"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/activity_main_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:cardCornerRadius="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/download_moreinfo_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtInfo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDownload"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/download_view_download"
|
||||
android:theme="@style/Widget.AppCompat.Button.Colored" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?listItemBackground"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="?downloadVerticalPadding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/downloadStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/description"
|
||||
android:layout_alignStart="@id/description"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timestamps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/downloadStatus"
|
||||
android:layout_alignStart="@id/description"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:minHeight="?attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtVersion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textIsSelectable="false" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="end"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtRelType"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAllCaps="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textIsSelectable="false"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtUploadDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
android:textIsSelectable="false"
|
||||
android:textStyle="italic" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<org.meowcat.edxposed.manager.ui.widget.DownloadView
|
||||
android:id="@+id/downloadView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtChangesTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/changes"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtChanges"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorStickyHeader"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dip"
|
||||
android:textAllCaps="true" />
|
||||
</LinearLayout>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_search"
|
||||
android:title="@string/menuSearch"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_sort"
|
||||
android:icon="@drawable/ic_sort"
|
||||
android:title="@string/download_sorting_title"
|
||||
app:showAsAction="ifRoom|withText" />
|
||||
|
||||
</menu>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_share"
|
||||
android:icon="@drawable/ic_share"
|
||||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_refresh"
|
||||
android:icon="@drawable/ic_refresh"
|
||||
android:title="@string/menuReload"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/ignoreUpdate"
|
||||
android:checkable="true"
|
||||
android:title="@string/ignore_updates" />
|
||||
|
||||
</menu>
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
<string-array name="download_sort_order" translatable="false">
|
||||
<item>@string/download_sorting_status</item>
|
||||
<item>@string/download_sorting_updated</item>
|
||||
<item>@string/download_sorting_created</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="theme_texts" translatable="false">
|
||||
<item>@string/follow_system</item>
|
||||
<item>@string/settings_theme_light</item>
|
||||
|
|
@ -19,52 +13,6 @@
|
|||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="release_type_texts" translatable="false">
|
||||
<item>@string/reltype_stable_summary</item>
|
||||
<item>@string/reltype_beta_summary</item>
|
||||
<item>@string/reltype_experimental_summary</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="release_type_values" translatable="false">
|
||||
<item>stable</item>
|
||||
<item>beta</item>
|
||||
<item>experimental</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="module_release_type_texts" translatable="false">
|
||||
<item>@string/reltype_use_global_summary</item>
|
||||
<item>@string/reltype_stable_summary</item>
|
||||
<item>@string/reltype_beta_summary</item>
|
||||
<item>@string/reltype_experimental_summary</item>
|
||||
</string-array>
|
||||
<string-array name="list_sort_texts">
|
||||
<item>@string/sort_by_name</item>
|
||||
<item>@string/sort_by_name_reverse</item>
|
||||
<item>@string/sort_by_package_name</item>
|
||||
<item>@string/sort_by_package_name_reverse</item>
|
||||
<item>@string/sort_by_install_time</item>
|
||||
<item>@string/sort_by_install_time_reverse</item>
|
||||
<item>@string/sort_by_update_time</item>
|
||||
<item>@string/sort_by_update_time_reverse</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="list_sort_values" translatable="false">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
</string-array>
|
||||
<string-array name="module_release_type_values" translatable="false">
|
||||
<item />
|
||||
<item>stable</item>
|
||||
<item>beta</item>
|
||||
<item>experimental</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="variant_texts" translatable="false">
|
||||
<item>YAHFA</item>
|
||||
<item>SandHook</item>
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<SimpleMenuPreference
|
||||
android:defaultValue=""
|
||||
android:entries="@array/module_release_type_texts"
|
||||
android:entryValues="@array/module_release_type_values"
|
||||
android:key="release_type"
|
||||
android:summary="%s"
|
||||
android:title="@string/settings_release_type"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Loading…
Reference in New Issue