Remove in app module download
This commit is contained in:
parent
abf4c9b546
commit
5c0a688ee5
|
|
@ -77,14 +77,6 @@
|
|||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receivers.DownloadReceiver"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".util.NotificationUtil$RebootReceiver"
|
||||
android:exported="false" />
|
||||
|
|
|
|||
|
|
@ -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<ModuleVersion> {
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Void, Void, Void> {
|
||||
private static class LongRunningTask implements Callable<Void> {
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, DownloadFinishedCallback> 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<DownloadInfo> all;
|
||||
try {
|
||||
all = getAllForUrl(context, url);
|
||||
} catch (Throwable throwable) {
|
||||
return null;
|
||||
}
|
||||
return Objects.requireNonNull(all).isEmpty() ? null : all.get(0);
|
||||
}
|
||||
|
||||
private static List<DownloadInfo> 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<DownloadInfo> 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<Long> 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<Long> 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<Long> 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<DownloadInfo> {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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<Void, Void, Integer> {
|
||||
|
||||
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<String> 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<Void, Void, Integer> {
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <R> void executeAsync(Callable<R> callable) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
callable.call();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -21,43 +20,5 @@
|
|||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/download_view_download"
|
||||
android:theme="@style/Widget.AppCompat.Button.Colored" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnSave"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/save"
|
||||
android:theme="@style/Widget.AppCompat.Button.Colored" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDownloadCancel"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/download_view_cancel"
|
||||
android:theme="@style/Widget.AppCompat.Button.Colored"
|
||||
android:visibility="gone"
|
||||
tools:ignore="ButtonOrder" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnInstall"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/download_view_install"
|
||||
android:theme="@style/Widget.AppCompat.Button.Colored"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</merge>
|
||||
|
|
@ -139,7 +139,7 @@
|
|||
<string name="about_libraries_label">使用的库</string>
|
||||
|
||||
<!-- DownloadView -->
|
||||
<string name="download_view_download">下载并安装</string>
|
||||
<string name="download_view_download">下载</string>
|
||||
<string name="download_view_install">安装</string>
|
||||
<string name="download_view_cancel">取消</string>
|
||||
<string name="download_view_no_url">无可用的下载链接</string>
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@
|
|||
<string name="about_libraries_label">使用的庫</string>
|
||||
|
||||
<!-- DownloadView -->
|
||||
<string name="download_view_download">下載並安裝</string>
|
||||
<string name="download_view_download">下載</string>
|
||||
<string name="download_view_install">安裝</string>
|
||||
<string name="download_view_cancel">取消</string>
|
||||
<string name="download_view_no_url">無可用的下載鏈接</string>
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@
|
|||
<string name="about_libraries_label">使用的庫</string>
|
||||
|
||||
<!-- DownloadView -->
|
||||
<string name="download_view_download">下載並安裝</string>
|
||||
<string name="download_view_download">下載</string>
|
||||
<string name="download_view_install">安裝</string>
|
||||
<string name="download_view_cancel">取消</string>
|
||||
<string name="download_view_no_url">無可用的下載連結</string>
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@
|
|||
<string name="about_libraries_label">Used libraries</string>
|
||||
|
||||
<!-- DownloadView -->
|
||||
<string name="download_view_download">Download and Install</string>
|
||||
<string name="download_view_download">Download</string>
|
||||
<string name="download_view_install">Install</string>
|
||||
<string name="download_view_cancel">Cancel</string>
|
||||
<string name="download_view_no_url">No download URL available</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue