diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3e9c1018..6088baa1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -77,14 +77,6 @@ - - - - - diff --git a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java index fcf79037..c17bbe3e 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/DownloadDetailsVersionsFragment.java @@ -1,13 +1,8 @@ package org.meowcat.edxposed.manager; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.res.Resources; -import android.net.Uri; import android.os.Bundle; import android.text.method.LinkMovementMethod; import android.util.TypedValue; @@ -24,23 +19,15 @@ import androidx.core.content.ContextCompat; import androidx.core.view.ViewCompat; import androidx.fragment.app.ListFragment; -import com.google.android.material.snackbar.Snackbar; - 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.util.DownloadsUtil; -import org.meowcat.edxposed.manager.util.HashUtil; -import org.meowcat.edxposed.manager.util.InstallApkUtil; import org.meowcat.edxposed.manager.util.ModuleUtil.InstalledModule; import org.meowcat.edxposed.manager.util.RepoLoader; import org.meowcat.edxposed.manager.util.chrome.LinkTransformationMethod; import org.meowcat.edxposed.manager.widget.DownloadView; -import java.io.File; -import java.io.FileInputStream; -import java.io.OutputStream; import java.text.DateFormat; import java.util.Date; @@ -72,7 +59,7 @@ public class DownloadDetailsVersionsFragment extends ListFragment { getListView().addHeaderView(txtHeader); } - VersionsAdapter sAdapter = new VersionsAdapter(activity, activity.getInstalledModule(), activity.findViewById(R.id.snackbar)); + VersionsAdapter sAdapter = new VersionsAdapter(activity, activity.getInstalledModule()/*, activity.findViewById(R.id.snackbar)*/); for (ModuleVersion version : module.versions) { if (repoLoader.isVersionShown(version)) sAdapter.add(version); @@ -104,36 +91,6 @@ public class DownloadDetailsVersionsFragment extends ListFragment { setListAdapter(null); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode != Activity.RESULT_OK) { - return; - } - if (requestCode == 42) { - if (data != null) { - Uri uri = data.getData(); - if (uri != null) { - try { - OutputStream os = activity.getContentResolver().openOutputStream(uri); - if (os != null) { - FileInputStream in = new FileInputStream(new File(DownloadView.lastInfo.localFilename)); - byte[] buffer = new byte[1024]; - int len; - while ((len = in.read(buffer)) > 0) { - os.write(buffer, 0, len); - } - os.close(); - } - } catch (Exception e) { - e.printStackTrace(); - //Snackbar.make(findViewById(R.id.snackbar), getResources().getString(R.string.logs_save_failed) + "\n" + e.getMessage(), Snackbar.LENGTH_LONG).show(); - } - } - } - } - } - static class ViewHolder { TextView txtStatus; TextView txtVersion; @@ -144,55 +101,6 @@ public class DownloadDetailsVersionsFragment extends ListFragment { TextView txtChanges; } - public static class DownloadModuleCallback implements DownloadsUtil.DownloadFinishedCallback { - private final ModuleVersion moduleVersion; - private View snackbar; - - DownloadModuleCallback(ModuleVersion moduleVersion, View snackbar) { - this.moduleVersion = moduleVersion; - this.snackbar = snackbar; - } - - @Override - public void onDownloadFinished(Context context, DownloadsUtil.DownloadInfo info) { - File localFile = new File(info.localFilename); - if (!localFile.isFile()) - return; - - if (moduleVersion.md5sum != null && !moduleVersion.md5sum.isEmpty()) { - try { - String actualMd5Sum = HashUtil.md5(localFile); - if (!moduleVersion.md5sum.equals(actualMd5Sum)) { - Snackbar.make(snackbar, context.getString(R.string.download_md5sum_incorrect, actualMd5Sum, moduleVersion.md5sum), Snackbar.LENGTH_LONG).show(); - DownloadsUtil.removeById(context, info.id); - return; - } - } catch (Exception e) { - Snackbar.make(snackbar, context.getString(R.string.download_could_not_read_file, e.getMessage()), Snackbar.LENGTH_LONG).show(); - DownloadsUtil.removeById(context, info.id); - return; - } - } - - PackageManager pm = context.getPackageManager(); - PackageInfo packageInfo = pm.getPackageArchiveInfo(info.localFilename, 0); - - if (packageInfo == null) { - Snackbar.make(snackbar, R.string.download_no_valid_apk, Snackbar.LENGTH_LONG).show(); - DownloadsUtil.removeById(context, info.id); - return; - } - - if (!packageInfo.packageName.equals(moduleVersion.module.packageName)) { - Snackbar.make(snackbar, context.getString(R.string.download_incorrect_package_name, packageInfo.packageName, moduleVersion.module.packageName), Snackbar.LENGTH_LONG).show(); - DownloadsUtil.removeById(context, info.id); - return; - } - - new InstallApkUtil(context, info).execute(); - } - } - private class VersionsAdapter extends ArrayAdapter { private final DateFormat dateFormatter = DateFormat .getDateInstance(DateFormat.SHORT); @@ -203,9 +111,9 @@ public class DownloadDetailsVersionsFragment extends ListFragment { private final String textInstalled; private final String textUpdateAvailable; private final long installedVersionCode; - private View snackbar; + //private View snackbar; - VersionsAdapter(Context context, InstalledModule installed, View snackbar) { + VersionsAdapter(Context context, InstalledModule installed/*, View snackbar*/) { super(context, R.layout.item_version); TypedValue typedValue = new TypedValue(); Resources.Theme theme = context.getTheme(); @@ -218,7 +126,7 @@ public class DownloadDetailsVersionsFragment extends ListFragment { textInstalled = getString(R.string.download_section_installed) + ":"; textUpdateAvailable = getString(R.string.download_section_update_available) + ":"; installedVersionCode = (installed != null) ? installed.versionCode : -1; - this.snackbar = snackbar; + //this.snackbar = snackbar; } @SuppressLint("InflateParams") @@ -274,7 +182,7 @@ public class DownloadDetailsVersionsFragment extends ListFragment { holder.downloadView.setUrl(item.downloadLink); holder.downloadView.setTitle(activity.getModule().name); - holder.downloadView.setDownloadFinishedCallback(new DownloadModuleCallback(item, snackbar)); + //holder.downloadView.setDownloadFinishedCallback(new DownloadModuleCallback(item, snackbar)); if (item.changelog != null && !item.changelog.isEmpty()) { holder.txtChangesTitle.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java index e2a2a902..b1292dde 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/ModulesActivity.java @@ -32,7 +32,6 @@ 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.util.DownloadsUtil; import org.meowcat.edxposed.manager.util.InstallApkUtil; import org.meowcat.edxposed.manager.util.ModuleUtil; import org.meowcat.edxposed.manager.util.NavUtil; @@ -369,7 +368,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi } if (mv != null) { - DownloadsUtil.addModule(this, m.name, mv.downloadLink, (context, info) -> new InstallApkUtil(this, info).execute()); + NavUtil.startURL(this, mv.downloadLink); } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/receivers/BootReceiver.java b/app/src/main/java/org/meowcat/edxposed/manager/receivers/BootReceiver.java index 3b6ee46c..c6764728 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/receivers/BootReceiver.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/receivers/BootReceiver.java @@ -3,27 +3,28 @@ package org.meowcat.edxposed.manager.receivers; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.AsyncTask; -import android.util.Log; import org.json.JSONObject; import org.meowcat.edxposed.manager.BuildConfig; -import org.meowcat.edxposed.manager.XposedApp; 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 android.os.Handler().postDelayed(() -> new CheckUpdates().execute(), 60 * 60 * 1000 /*60 min*/); + new TaskRunner().executeAsync(new LongRunningTask()); } - private static class CheckUpdates extends AsyncTask { + private static class LongRunningTask implements Callable { @Override - protected Void doInBackground(Void... params) { + 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"); @@ -35,11 +36,9 @@ public class BootReceiver extends BroadcastReceiver { NotificationUtil.showInstallerUpdateNotification(); } } catch (Exception e) { - //noinspection ConstantConditions - Log.d(XposedApp.TAG, e.getMessage()); + e.printStackTrace(); } return null; } - } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/receivers/DownloadReceiver.java b/app/src/main/java/org/meowcat/edxposed/manager/receivers/DownloadReceiver.java deleted file mode 100644 index b24ca571..00000000 --- a/app/src/main/java/org/meowcat/edxposed/manager/receivers/DownloadReceiver.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.meowcat.edxposed.manager.receivers; - -import android.app.DownloadManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.widget.Toast; - -import org.meowcat.edxposed.manager.util.DownloadsUtil; - -public class DownloadReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, final Intent intent) { - try { - String action = intent.getAction(); - if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { - long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); - DownloadsUtil.triggerDownloadFinishedCallback(context, downloadId); - } - } catch (Exception e) {//Flyme - e.printStackTrace(); - Toast.makeText(context, "shit flyme boom", Toast.LENGTH_LONG).show(); - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java b/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java index 68d05c61..dd251424 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/util/DownloadsUtil.java @@ -1,17 +1,7 @@ package org.meowcat.edxposed.manager.util; -import android.app.DownloadManager; -import android.app.DownloadManager.Query; -import android.app.DownloadManager.Request; import android.content.Context; import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore; -import android.util.Log; -import android.widget.Toast; - -import androidx.annotation.NonNull; import org.meowcat.edxposed.manager.R; import org.meowcat.edxposed.manager.XposedApp; @@ -23,303 +13,10 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; public class DownloadsUtil { - public static final String MIME_TYPE_APK = "application/vnd.android.package-archive"; - private static final Map callbacks = new HashMap<>(); private static final SharedPreferences pref = XposedApp.getInstance().getSharedPreferences("download_cache", Context.MODE_PRIVATE); - private static DownloadInfo add(Builder b) { - Context context = b.context; - removeAllForUrl(context, b.url); - - synchronized (callbacks) { - callbacks.put(b.url, b.callback); - } - - Request request = new Request(Uri.parse(b.url)); - request.setTitle(b.title); - request.setMimeType(b.mimeType.toString()); - request.setNotificationVisibility(Request.VISIBILITY_VISIBLE); - File path = new File(context.getExternalCacheDir(), "downloads"); - try { - if (!path.mkdirs()) return null; - }catch (Exception e) { - e.printStackTrace(); - return null; - } - File destination = new File(path, b.title + b.mimeType.getExtension()); - removeAllForLocalFile(context, destination); - request.setDestinationUri(Uri.fromFile(destination)); - DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - long id = dm.enqueue(request); - return getById(context, id); - } - - public static DownloadInfo addModule(Context context, String title, String url, DownloadFinishedCallback callback) { - return new Builder(context) - .setTitle(title) - .setUrl(url) - .setCallback(callback) - .setModule(true) - .setMimeType(MIME_TYPES.APK) - .download(); - } - - /* - public static ModuleVersion getStableVersion(Module m) { - for (int i = 0; i < m.versions.size(); i++) { - ModuleVersion mvTemp = m.versions.get(i); - - if (mvTemp.relType == ReleaseType.STABLE) { - return mvTemp; - } - } - return null; - } - */ - public static DownloadInfo getById(Context context, long id) { - DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - Cursor c = dm.query(new Query().setFilterById(id)); - if (!c.moveToFirst()) { - c.close(); - return null; - } - - int columnUri = c.getColumnIndexOrThrow(DownloadManager.COLUMN_URI); - int columnTitle = c.getColumnIndexOrThrow(DownloadManager.COLUMN_TITLE); - int columnLastMod = c.getColumnIndexOrThrow( - DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP); - int columnLocalUri = c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI); - int columnStatus = c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS); - int columnTotalSize = c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES); - int columnBytesDownloaded = c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR); - int columnReason = c.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON); - - int status = c.getInt(columnStatus); - String localFilename; - try { - localFilename = getFilenameFromUri(c.getString(columnLocalUri)); - } catch (UnsupportedOperationException e) { - Toast.makeText(context, "An error occurred. Restart app and try again.\n" + e.getMessage(), Toast.LENGTH_SHORT).show(); - return null; - } - if (status == DownloadManager.STATUS_SUCCESSFUL && !new File(localFilename).isFile()) { - dm.remove(id); - c.close(); - return null; - } - - DownloadInfo info = new DownloadInfo(id, c.getString(columnUri), - c.getString(columnTitle), c.getLong(columnLastMod), - localFilename, status, - c.getInt(columnTotalSize), c.getInt(columnBytesDownloaded), - c.getInt(columnReason)); - c.close(); - return info; - } - - public static DownloadInfo getLatestForUrl(Context context, String url) { - List all; - try { - all = getAllForUrl(context, url); - } catch (Throwable throwable) { - return null; - } - return Objects.requireNonNull(all).isEmpty() ? null : all.get(0); - } - - private static List getAllForUrl(Context context, String url) { - DownloadManager dm = (DownloadManager) context - .getSystemService(Context.DOWNLOAD_SERVICE); - Cursor c = dm.query(new Query()); - int columnId = c.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); - int columnUri = c.getColumnIndexOrThrow(DownloadManager.COLUMN_URI); - int columnTitle = c.getColumnIndexOrThrow(DownloadManager.COLUMN_TITLE); - int columnLastMod = c.getColumnIndexOrThrow( - DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP); - int columnLocalUri = c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI); - int columnStatus = c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS); - int columnTotalSize = c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES); - int columnBytesDownloaded = c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR); - int columnReason = c.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON); - - List downloads = new ArrayList<>(); - while (c.moveToNext()) { - if (!url.equals(c.getString(columnUri))) - continue; - - int status = c.getInt(columnStatus); - String localFilename; - try { - localFilename = getFilenameFromUri(c.getString(columnLocalUri)); - } catch (UnsupportedOperationException e) { - Toast.makeText(context, "An error occurred. Restart app and try again.\n" + e.getMessage(), Toast.LENGTH_SHORT).show(); - return null; - } - if (status == DownloadManager.STATUS_SUCCESSFUL && !new File(localFilename).isFile()) { - dm.remove(c.getLong(columnId)); - continue; - } - - downloads.add(new DownloadInfo(c.getLong(columnId), - c.getString(columnUri), c.getString(columnTitle), - c.getLong(columnLastMod), localFilename, - status, c.getInt(columnTotalSize), - c.getInt(columnBytesDownloaded), c.getInt(columnReason))); - } - c.close(); - - Collections.sort(downloads); - return downloads; - } - - public static void removeById(Context context, long id) { - DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - dm.remove(id); - } - - private static void removeAllForUrl(Context context, String url) { - DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - Cursor c = dm.query(new Query()); - int columnId = c.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); - int columnUri = c.getColumnIndexOrThrow(DownloadManager.COLUMN_URI); - - List idsList = new ArrayList<>(1); - while (c.moveToNext()) { - if (url.equals(c.getString(columnUri))) - idsList.add(c.getLong(columnId)); - } - c.close(); - - if (idsList.isEmpty()) - return; - - long[] ids = new long[idsList.size()]; - for (int i = 0; i < ids.length; i++) - ids[i] = idsList.get(i); - - dm.remove(ids); - } - - private static void removeAllForLocalFile(Context context, File file) { - //noinspection ResultOfMethodCallIgnored - file.delete(); - - String filename; - try { - filename = file.getCanonicalPath(); - } catch (IOException e) { - Log.w(XposedApp.TAG, "Could not resolve path for " + file.getAbsolutePath(), e); - return; - } - - DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - Cursor c = dm.query(new Query()); - int columnId = c.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); - int columnLocalUri = c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI); - - List idsList = new ArrayList<>(1); - while (c.moveToNext()) { - String itemFilename; - try { - itemFilename = getFilenameFromUri(c.getString(columnLocalUri)); - } catch (UnsupportedOperationException e) { - Toast.makeText(context, "An error occurred. Restart app and try again.\n" + e.getMessage(), Toast.LENGTH_SHORT).show(); - itemFilename = null; - } - if (itemFilename != null) { - if (filename.equals(itemFilename)) { - idsList.add(c.getLong(columnId)); - } else { - try { - if (filename.equals(new File(itemFilename).getCanonicalPath())) { - idsList.add(c.getLong(columnId)); - } - } catch (IOException ignored) { - } - } - } - } - c.close(); - - if (idsList.isEmpty()) - return; - - long[] ids = new long[idsList.size()]; - for (int i = 0; i < ids.length; i++) - ids[i] = idsList.get(i); - - dm.remove(ids); - } - -// public static void removeOutdated(Context context, long cutoff) { -// DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); -// Cursor c = dm.query(new Query()); -// int columnId = c.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); -// int columnLastMod = c.getColumnIndexOrThrow( -// DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP); -// -// List idsList = new ArrayList<>(); -// while (c.moveToNext()) { -// if (c.getLong(columnLastMod) < cutoff) -// idsList.add(c.getLong(columnId)); -// } -// c.close(); -// -// if (idsList.isEmpty()) -// return; -// -// long[] ids = new long[idsList.size()]; -// for (int i = 0; i < ids.length; i++) -// ids[i] = idsList.get(0); -// -// dm.remove(ids); -// } - - public static void triggerDownloadFinishedCallback(Context context, long id) { - DownloadInfo info = getById(context, id); - if (info == null || info.status != DownloadManager.STATUS_SUCCESSFUL) - return; - - DownloadFinishedCallback callback; - synchronized (callbacks) { - callback = callbacks.get(info.url); - } - - if (callback == null) - return; - - // Hack to reset stat information. - //noinspection ResultOfMethodCallIgnored - new File(info.localFilename).setExecutable(false); - callback.onDownloadFinished(context, info); - } - - private static String getFilenameFromUri(String uriString) { - if (uriString == null) { - return null; - } - Uri uri = Uri.parse(uriString); - if (Objects.requireNonNull(uri.getScheme()).equals("file")) { - return uri.getPath(); - } else if (uri.getScheme().equals("content")) { - Context context = XposedApp.getInstance(); - try (Cursor c = context.getContentResolver().query(uri, new String[]{MediaStore.Files.FileColumns.DATA}, null, null, null)) { - Objects.requireNonNull(c).moveToFirst(); - return c.getString(c.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)); - } - } else { - throw new UnsupportedOperationException("Unexpected URI: " + uriString); - } - } - static SyncDownloadInfo downloadSynchronously(String url, File target) { final boolean useNotModifiedTags = target.exists(); @@ -416,111 +113,6 @@ public class DownloadsUtil { } } - public enum MIME_TYPES { - APK { - @NonNull - public String toString() { - return MIME_TYPE_APK; - } - - public String getExtension() { - return ".apk"; - } - }; -// ZIP { -// public String toString() { -// return MIME_TYPE_ZIP; -// } -// -// public String getExtension() { -// return ".zip"; -// } -// }; - - public String getExtension() { - return null; - } - } - - public interface DownloadFinishedCallback { - void onDownloadFinished(Context context, DownloadInfo info); - } - - public static class Builder { - private final Context context; - boolean module = false; - private String title = null; - private String url = null; - private DownloadFinishedCallback callback = null; - private MIME_TYPES mimeType = MIME_TYPES.APK; - - public Builder(Context context) { - this.context = context; - } - - public Builder setTitle(String title) { - this.title = title; - return this; - } - - public Builder setUrl(String url) { - this.url = url; - return this; - } - - public Builder setCallback(DownloadFinishedCallback callback) { - this.callback = callback; - return this; - } - - Builder setMimeType(MIME_TYPES mimeType) { - this.mimeType = mimeType; - return this; - } - - public Builder setModule(boolean module) { - this.module = module; - return this; - } - - public DownloadInfo download() { - return add(this); - } - } - - public static class DownloadInfo implements Comparable { - public final long id; - public final String url; - public final String title; - public final String localFilename; - public final int status; - public final int totalSize; - public final int bytesDownloaded; - public final int reason; - final long lastModification; - - private DownloadInfo(long id, String url, String title, long lastModification, String localFilename, int status, int totalSize, int bytesDownloaded, int reason) { - this.id = id; - this.url = url; - this.title = title; - this.lastModification = lastModification; - this.localFilename = localFilename; - this.status = status; - this.totalSize = totalSize; - this.bytesDownloaded = bytesDownloaded; - this.reason = reason; - } - - @Override - public int compareTo(@NonNull DownloadInfo another) { - int compare = (int) (another.lastModification - - this.lastModification); - if (compare != 0) - return compare; - return this.url.compareTo(another.url); - } - } - public static class SyncDownloadInfo { static final int STATUS_SUCCESS = 0; static final int STATUS_NOT_MODIFIED = 1; diff --git a/app/src/main/java/org/meowcat/edxposed/manager/util/InstallApkUtil.java b/app/src/main/java/org/meowcat/edxposed/manager/util/InstallApkUtil.java index c59b436e..8a05b6f2 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/util/InstallApkUtil.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/util/InstallApkUtil.java @@ -1,41 +1,10 @@ package org.meowcat.edxposed.manager.util; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.net.Uri; -import android.os.AsyncTask; -import androidx.core.content.FileProvider; - -import com.topjohnwu.superuser.Shell; - -import org.meowcat.edxposed.manager.BuildConfig; -import org.meowcat.edxposed.manager.R; -import org.meowcat.edxposed.manager.XposedApp; - -import java.io.File; -import java.util.LinkedList; -import java.util.List; - -public class InstallApkUtil extends AsyncTask { - - private static final int ERROR_ROOT_NOT_GRANTED = -99; - - private final DownloadsUtil.DownloadInfo info; - @SuppressLint("StaticFieldLeak") - private final Context context; - private boolean isApkRootInstallOn; - private List output = new LinkedList<>(); - - public InstallApkUtil(Context context, DownloadsUtil.DownloadInfo info) { - this.context = context; - this.info = info; - } +public class InstallApkUtil { public static String getAppLabel(ApplicationInfo info, PackageManager pm) { try { @@ -47,74 +16,4 @@ public class InstallApkUtil extends AsyncTask { } return info.loadLabel(pm).toString(); } - - static void installApkNormally(Context context, String localFilename) { - Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE); - installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", new File(localFilename)); - installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - installIntent.setDataAndType(uri, DownloadsUtil.MIME_TYPE_APK); - installIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.getApplicationInfo().packageName); - context.startActivity(installIntent); - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - - SharedPreferences prefs = XposedApp.getPreferences(); - isApkRootInstallOn = prefs.getBoolean("install_with_su", false); - - if (isApkRootInstallOn) { - NotificationUtil.showModuleInstallingNotification(info.title); - } - } - - @Override - protected Integer doInBackground(Void... params) { - int returnCode = 0; - if (isApkRootInstallOn) { - try { - String path = "/data/local/tmp/"; - String fileName = new File(info.localFilename).getName(); - Shell.su("cat \"" + info.localFilename + "\">" + path + fileName).exec(); - Shell.Result result = Shell.su("pm install -r -f \"" + path + fileName + "\"").exec(); - returnCode = result.getCode(); - output = result.getOut(); - Shell.su("rm -f " + path + fileName).exec(); - } catch (IllegalStateException e) { - returnCode = ERROR_ROOT_NOT_GRANTED; - } - } - return returnCode; - } - - @Override - protected void onPostExecute(Integer result) { - super.onPostExecute(result); - - if (isApkRootInstallOn) { - NotificationUtil.cancel(NotificationUtil.NOTIFICATION_MODULE_INSTALLING); - - if (result.equals(ERROR_ROOT_NOT_GRANTED)) { - NotificationUtil.showModuleInstallNotification(R.string.installation_error, R.string.root_failed, info.localFilename); - return; - } - - StringBuilder out = new StringBuilder(); - for (String o : output) { - out.append(o); - out.append("\n"); - } - - if (result.equals(0)) { - NotificationUtil.showModuleInstallNotification(R.string.installation_successful, R.string.installation_successful_message, info.localFilename, info.title); - } else { - NotificationUtil.showModuleInstallNotification(R.string.installation_error, R.string.installation_error_message, info.localFilename, info.title, out); - installApkNormally(context, info.localFilename); - } - } else { - installApkNormally(context, info.localFilename); - } - } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/util/NotificationUtil.java b/app/src/main/java/org/meowcat/edxposed/manager/util/NotificationUtil.java index e7ea200b..c08df26b 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/util/NotificationUtil.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/util/NotificationUtil.java @@ -12,7 +12,6 @@ import android.os.Build; import android.util.Log; import android.widget.Toast; -import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; @@ -25,17 +24,14 @@ import org.meowcat.edxposed.manager.XposedApp; public final class NotificationUtil { public static final int NOTIFICATION_MODULE_NOT_ACTIVATED_YET = 0; - public static final int NOTIFICATION_MODULE_INSTALLING = 4; private static final int NOTIFICATION_MODULES_UPDATED = 1; private static final int NOTIFICATION_INSTALLER_UPDATE = 2; - private static final int NOTIFICATION_MODULE_INSTALLATION = 3; 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; private static final int PENDING_INTENT_REBOOT = 3; private static final int PENDING_INTENT_ACTIVATE_MODULE_AND_REBOOT = 4; private static final int PENDING_INTENT_ACTIVATE_MODULE = 5; - private static final int PENDING_INTENT_INSTALL_APK = 6; private static final String HEADS_UP = "heads_up"; private static final String FRAGMENT_ID = "fragment"; @@ -65,10 +61,6 @@ public final class NotificationUtil { } } - public static void cancel(int id) { - notificationManager.cancel(id); - } - public static void cancel(String tag, int id) { notificationManager.cancel(tag, id); } @@ -145,48 +137,6 @@ public final class NotificationUtil { notificationManager.notify(null, NOTIFICATION_MODULES_UPDATED, builder.build()); } - static void showModuleInstallNotification(@StringRes int title, @StringRes int message, String path, Object... args) { - showModuleInstallNotification(context.getString(title), context.getString(message, args), path, title == R.string.installation_error); - } - - private static void showModuleInstallNotification(String title, String message, String path, boolean error) { - NotificationCompat.Builder builder = getNotificationBuilder(title, message, NOTIFICATION_MODULES_CHANNEL); - - if (error) { - Intent iInstallApk = new Intent(context, ApkReceiver.class); - iInstallApk.putExtra(ApkReceiver.EXTRA_APK_PATH, path); - PendingIntent pInstallApk = PendingIntent.getBroadcast(context, PENDING_INTENT_INSTALL_APK, iInstallApk, PendingIntent.FLAG_UPDATE_CURRENT); - - builder.addAction(new NotificationCompat.Action.Builder(0, context.getString(R.string.installation_apk_normal), pInstallApk).build()); - } - - 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_MODULE_INSTALLATION, builder.build()); - - new android.os.Handler().postDelayed(() -> cancel(NOTIFICATION_MODULE_INSTALLATION), 10 * 1000); - } - - public static void showModuleInstallingNotification(String appName) { - String title = context.getString(R.string.install_load); - String message = context.getString(R.string.install_load_apk, appName); - NotificationCompat.Builder builder = getNotificationBuilder(title, message, NOTIFICATION_MODULES_CHANNEL) - .setProgress(0, 0, true) - .setOngoing(true); - NotificationCompat.BigTextStyle notiStyle = new NotificationCompat.BigTextStyle(); - notiStyle.setBigContentTitle(title); - notiStyle.bigText(message); - builder.setStyle(notiStyle); - - notificationManager.notify(null, NOTIFICATION_MODULE_INSTALLING, builder.build()); - } - public static void showInstallerUpdateNotification() { Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -264,26 +214,4 @@ public final class NotificationUtil { } } } - - public static class ApkReceiver extends BroadcastReceiver { - public static final String EXTRA_APK_PATH = "path"; - - @Override - public void onReceive(Context context, Intent intent) { - /* - * Close the notification bar in order to see the toast that module - * was enabled successfully. Furthermore, if SU permissions haven't - * been granted yet, the SU dialog will be prompted behind the - * expanded notification panel and is therefore not visible to the - * user. - */ - context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - - if (intent.hasExtra(EXTRA_APK_PATH)) { - String path = intent.getStringExtra(EXTRA_APK_PATH); - InstallApkUtil.installApkNormally(context, path); - } - NotificationUtil.cancel(NotificationUtil.NOTIFICATION_MODULE_INSTALLATION); - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/meowcat/edxposed/manager/util/TaskRunner.java b/app/src/main/java/org/meowcat/edxposed/manager/util/TaskRunner.java new file mode 100644 index 00000000..fbdbe1ac --- /dev/null +++ b/app/src/main/java/org/meowcat/edxposed/manager/util/TaskRunner.java @@ -0,0 +1,19 @@ +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 void executeAsync(Callable callable) { + executor.execute(() -> { + try { + callable.call(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java b/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java index a9bf8ae3..ffdbc3a0 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/widget/DownloadView.java @@ -1,8 +1,6 @@ package org.meowcat.edxposed.manager.widget; -import android.app.DownloadManager; import android.content.Context; -import android.content.Intent; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -10,86 +8,16 @@ import android.widget.LinearLayout; import androidx.fragment.app.Fragment; +import org.meowcat.edxposed.manager.BaseActivity; import org.meowcat.edxposed.manager.R; import org.meowcat.edxposed.manager.databinding.DownloadViewBinding; -import org.meowcat.edxposed.manager.util.DownloadsUtil; -import org.meowcat.edxposed.manager.util.DownloadsUtil.DownloadFinishedCallback; +import org.meowcat.edxposed.manager.util.NavUtil; public class DownloadView extends LinearLayout { - public static DownloadsUtil.DownloadInfo lastInfo = null; public Fragment fragment; - private DownloadsUtil.DownloadInfo mInfo = null; private String mUrl = null; private String mTitle = null; - private DownloadFinishedCallback mCallback = null; private DownloadViewBinding binding; - private final Runnable refreshViewRunnable = new Runnable() { - @Override - public void run() { - if (mUrl == null) { - binding.btnDownload.setVisibility(View.GONE); - binding.btnSave.setVisibility(View.GONE); - binding.btnDownloadCancel.setVisibility(View.GONE); - binding.btnInstall.setVisibility(View.GONE); - binding.progress.setVisibility(View.GONE); - binding.txtInfo.setVisibility(View.VISIBLE); - binding.txtInfo.setText(R.string.download_view_no_url); - } else if (mInfo == null) { - binding.btnDownload.setVisibility(View.VISIBLE); - binding.btnSave.setVisibility(View.VISIBLE); - binding.btnDownloadCancel.setVisibility(View.GONE); - binding.btnInstall.setVisibility(View.GONE); - binding.progress.setVisibility(View.GONE); - binding.txtInfo.setVisibility(View.GONE); - } else { - switch (mInfo.status) { - case DownloadManager.STATUS_PENDING: - case DownloadManager.STATUS_PAUSED: - case DownloadManager.STATUS_RUNNING: - binding.btnDownload.setVisibility(View.GONE); - binding.btnSave.setVisibility(View.GONE); - binding.btnDownloadCancel.setVisibility(View.VISIBLE); - binding.btnInstall.setVisibility(View.GONE); - binding.progress.setVisibility(View.VISIBLE); - binding.txtInfo.setVisibility(View.VISIBLE); - if (mInfo.totalSize <= 0 || mInfo.status != DownloadManager.STATUS_RUNNING) { - binding.progress.setIndeterminate(true); - binding.txtInfo.setText(R.string.download_view_waiting); - } else { - binding.progress.setIndeterminate(false); - binding.progress.setMax(mInfo.totalSize); - binding.progress.setProgress(mInfo.bytesDownloaded); - binding.txtInfo.setText(getContext().getString( - R.string.download_view_running, - mInfo.bytesDownloaded / 1024, - mInfo.totalSize / 1024)); - } - break; - - case DownloadManager.STATUS_FAILED: - binding.btnDownload.setVisibility(View.VISIBLE); - binding.btnSave.setVisibility(View.VISIBLE); - binding.btnDownloadCancel.setVisibility(View.GONE); - binding.btnInstall.setVisibility(View.GONE); - binding.progress.setVisibility(View.GONE); - binding.txtInfo.setVisibility(View.VISIBLE); - binding.txtInfo.setText(getContext().getString( - R.string.download_view_failed, mInfo.reason)); - break; - - case DownloadManager.STATUS_SUCCESSFUL: - binding.btnDownload.setVisibility(View.GONE); - binding.btnSave.setVisibility(View.VISIBLE); - binding.btnDownloadCancel.setVisibility(View.GONE); - binding.btnInstall.setVisibility(View.VISIBLE); - binding.progress.setVisibility(View.GONE); - binding.txtInfo.setVisibility(View.VISIBLE); - binding.txtInfo.setText(R.string.download_view_successful); - break; - } - } - } - }; public DownloadView(Context context, final AttributeSet attrs) { super(context, attrs); @@ -98,51 +26,7 @@ public class DownloadView extends LinearLayout { binding = DownloadViewBinding.inflate(LayoutInflater.from(context), this); - binding.btnDownload.setOnClickListener(v -> { - mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, mCallback); - refreshViewFromUiThread(); - if (mInfo != null) - new DownloadMonitor().start(); - }); - - binding.btnSave.setOnClickListener(v -> { - lastInfo = mInfo; - mInfo = DownloadsUtil.addModule(getContext(), mTitle, mUrl, (context1, info) -> { - Intent exportIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - exportIntent.addCategory(Intent.CATEGORY_OPENABLE); - exportIntent.setType(DownloadsUtil.MIME_TYPE_APK); - exportIntent.putExtra(Intent.EXTRA_TITLE, mTitle + ".apk"); - fragment.startActivityForResult(exportIntent, 42); - }); - refreshViewFromUiThread(); - if (mInfo != null) - new DownloadMonitor().start(); - }); - - binding.btnDownloadCancel.setOnClickListener(v -> { - if (mInfo == null) - return; - - DownloadsUtil.removeById(getContext(), mInfo.id); - // UI update will happen automatically by the DownloadMonitor - }); - - binding.btnInstall.setOnClickListener(v -> { - if (mCallback == null) - return; - - mCallback.onDownloadFinished(getContext(), mInfo); - }); - - refreshViewFromUiThread(); - } - - private void refreshViewFromUiThread() { - refreshViewRunnable.run(); - } - - private void refreshView() { - post(refreshViewRunnable); + binding.btnDownload.setOnClickListener(v -> NavUtil.startURL((BaseActivity) context, mUrl)); } public String getUrl() { @@ -151,13 +35,14 @@ public class DownloadView extends LinearLayout { public void setUrl(String url) { mUrl = url; - - if (mUrl != null) - mInfo = DownloadsUtil.getLatestForUrl(getContext(), mUrl); - else - mInfo = null; - - refreshView(); + 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() { @@ -167,44 +52,4 @@ public class DownloadView extends LinearLayout { public void setTitle(String title) { this.mTitle = title; } - - @SuppressWarnings("unused") - public DownloadFinishedCallback getDownloadFinishedCallback() { - return mCallback; - } - - public void setDownloadFinishedCallback(DownloadFinishedCallback downloadFinishedCallback) { - this.mCallback = downloadFinishedCallback; - } - - private class DownloadMonitor extends Thread { - DownloadMonitor() { - super("DownloadMonitor"); - } - - @Override - public void run() { - while (true) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - return; - } - - try { - mInfo = DownloadsUtil.getById(getContext(), mInfo.id); - } catch (NullPointerException ignored) { - } - - refreshView(); - if (mInfo == null) - return; - - if (mInfo.status != DownloadManager.STATUS_PENDING - && mInfo.status != DownloadManager.STATUS_PAUSED - && mInfo.status != DownloadManager.STATUS_RUNNING) - return; - } - } - } } \ No newline at end of file diff --git a/app/src/main/res/layout/download_view.xml b/app/src/main/res/layout/download_view.xml index 6c49b174..a4b7fd6d 100644 --- a/app/src/main/res/layout/download_view.xml +++ b/app/src/main/res/layout/download_view.xml @@ -1,5 +1,4 @@ - + - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 6d1defb0..825c5193 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -139,7 +139,7 @@ 使用的库 - 下载并安装 + 下载 安装 取消 无可用的下载链接 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 5bd48679..5813b89b 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -139,7 +139,7 @@ 使用的庫 - 下載並安裝 + 下載 安裝 取消 無可用的下載鏈接 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 19bbb077..4cd5ac88 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -139,7 +139,7 @@ 使用的庫 - 下載並安裝 + 下載 安裝 取消 無可用的下載連結 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ee41c95..0d95b6ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -141,7 +141,7 @@ Used libraries - Download and Install + Download Install Cancel No download URL available