Use notifications when downloading modules
This commit is contained in:
parent
422c24bd68
commit
3af66b72f2
@ -12,8 +12,8 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.asyncs.DownloadModule;
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
|
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
@ -105,16 +105,15 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
|
|
||||||
holder.downloadImage.setOnClickListener(v -> {
|
holder.downloadImage.setOnClickListener(v -> {
|
||||||
new CustomAlertDialog((BaseActivity) context)
|
new CustomAlertDialog((BaseActivity) context)
|
||||||
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
|
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
|
||||||
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
|
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setPositiveButton(R.string.install, (d, i) ->
|
.setPositiveButton(R.string.install, (d, i) ->
|
||||||
new ProcessRepoZip((BaseActivity) context, repo, true).exec()
|
DownloadModule.exec((BaseActivity) context, repo, true))
|
||||||
)
|
.setNeutralButton(R.string.download, (d, i) ->
|
||||||
.setNeutralButton(R.string.download, (d, i) ->
|
DownloadModule.exec((BaseActivity) context, repo, false))
|
||||||
new ProcessRepoZip((BaseActivity) context, repo, false).exec())
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
.show();
|
||||||
.show();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
import com.topjohnwu.magisk.components.NotificationProgress;
|
||||||
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
public class DownloadModule {
|
||||||
|
|
||||||
|
public static void exec(BaseActivity activity, Repo repo, boolean install) {
|
||||||
|
activity.runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
|
||||||
|
() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> dlProcessInstall(repo, install)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dlProcessInstall(Repo repo, boolean install) {
|
||||||
|
File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
|
||||||
|
NotificationProgress progress = new NotificationProgress(output.getName());
|
||||||
|
try {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
File temp1 = new File(mm.getCacheDir(), "temp.zip");
|
||||||
|
|
||||||
|
HttpURLConnection conn = WebService.mustRequest(repo.getZipUrl());
|
||||||
|
ProgressInputStream pis = new ProgressInputStream(conn.getInputStream(),
|
||||||
|
conn.getContentLength(), progress);
|
||||||
|
removeTopFolder(new BufferedInputStream(pis),
|
||||||
|
new BufferedOutputStream(new FileOutputStream(temp1)));
|
||||||
|
conn.disconnect();
|
||||||
|
progress.getNotification()
|
||||||
|
.setProgress(0, 0, true)
|
||||||
|
.setContentTitle(mm.getString(R.string.zip_process_msg))
|
||||||
|
.setContentText("");
|
||||||
|
progress.update();
|
||||||
|
ZipUtils.signZip(temp1, output);
|
||||||
|
temp1.delete();
|
||||||
|
if (install) {
|
||||||
|
progress.dismiss();
|
||||||
|
Intent intent = new Intent(mm, Data.classMap.get(FlashActivity.class));
|
||||||
|
intent.setData(Uri.fromFile(output))
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||||
|
mm.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
progress.getNotification().setContentTitle(output.getName());
|
||||||
|
progress.dlDone();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
progress.dlFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeTopFolder(InputStream in, OutputStream out) throws IOException {
|
||||||
|
try (ZipInputStream zin = new ZipInputStream(in);
|
||||||
|
ZipOutputStream zout = new ZipOutputStream(out)) {
|
||||||
|
ZipEntry entry;
|
||||||
|
int off = -1;
|
||||||
|
while ((entry = zin.getNextEntry()) != null) {
|
||||||
|
if (off < 0)
|
||||||
|
off = entry.getName().indexOf('/') + 1;
|
||||||
|
String path = entry.getName().substring(off);
|
||||||
|
if (path.isEmpty())
|
||||||
|
continue;
|
||||||
|
zout.putNextEntry(new ZipEntry(path));
|
||||||
|
if (!entry.isDirectory())
|
||||||
|
ShellUtils.pump(zin, zout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ProgressInputStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private long totalBytes;
|
||||||
|
private long bytesDownloaded;
|
||||||
|
private NotificationProgress progress;
|
||||||
|
|
||||||
|
protected ProgressInputStream(InputStream in, long size, NotificationProgress p) {
|
||||||
|
super(in);
|
||||||
|
totalBytes = size;
|
||||||
|
progress = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProgress() {
|
||||||
|
progress.onProgress(bytesDownloaded, totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int b = super.read();
|
||||||
|
if (b >= 0) {
|
||||||
|
bytesDownloaded++;
|
||||||
|
updateProgress();
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
int sz = super.read(b, off, len);
|
||||||
|
if (sz > 0) {
|
||||||
|
bytesDownloaded += sz;
|
||||||
|
updateProgress();
|
||||||
|
}
|
||||||
|
return sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,196 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.Data;
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.jar.JarInputStream;
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|
||||||
|
|
||||||
private ProgressDialog progressDialog;
|
|
||||||
private boolean mInstall;
|
|
||||||
private File mFile;
|
|
||||||
private Repo mRepo;
|
|
||||||
private int progress = 0, total = -1;
|
|
||||||
|
|
||||||
public ProcessRepoZip(BaseActivity context, Repo repo, boolean install) {
|
|
||||||
super(context);
|
|
||||||
mRepo = repo;
|
|
||||||
mInstall = install && Shell.rootAccess();
|
|
||||||
mFile = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeTopFolder(File input, File output) throws IOException {
|
|
||||||
JarEntry entry;
|
|
||||||
try (
|
|
||||||
JarInputStream in = new JarInputStream(new BufferedInputStream(new FileInputStream(input)));
|
|
||||||
JarOutputStream out = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(output)))
|
|
||||||
) {
|
|
||||||
String path;
|
|
||||||
while ((entry = in.getNextJarEntry()) != null) {
|
|
||||||
// Remove the top directory from the path
|
|
||||||
path = entry.getName().substring(entry.getName().indexOf("/") + 1);
|
|
||||||
// If it's the top folder, ignore it
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Don't include placeholder
|
|
||||||
if (path.equals("system/placeholder")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out.putNextEntry(new JarEntry(path));
|
|
||||||
ShellUtils.pump(in, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BaseActivity getActivity() {
|
|
||||||
return (BaseActivity) super.getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
BaseActivity activity = getActivity();
|
|
||||||
mFile.getParentFile().mkdirs();
|
|
||||||
progressDialog = ProgressDialog.show(activity, activity.getString(R.string.zip_download_title), activity.getString(R.string.zip_download_msg, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
BaseActivity activity = getActivity();
|
|
||||||
if (activity == null) return null;
|
|
||||||
try {
|
|
||||||
// Request zip from Internet
|
|
||||||
HttpURLConnection conn = WebService.mustRequest(mRepo.getZipUrl());
|
|
||||||
total = conn.getContentLength();
|
|
||||||
|
|
||||||
// Temp files
|
|
||||||
File temp1 = new File(activity.getCacheDir(), "1.zip");
|
|
||||||
File temp2 = new File(temp1.getParentFile(), "2.zip");
|
|
||||||
temp1.getParentFile().mkdir();
|
|
||||||
|
|
||||||
// First upgrade the zip, Web -> temp1
|
|
||||||
try (
|
|
||||||
InputStream in = new BufferedInputStream(new ProgressInputStream(conn.getInputStream()));
|
|
||||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))
|
|
||||||
) {
|
|
||||||
ShellUtils.pump(in, out);
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
conn.disconnect();
|
|
||||||
|
|
||||||
Data.mainHandler.post(() -> {
|
|
||||||
progressDialog.setTitle(R.string.zip_process_title);
|
|
||||||
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
|
|
||||||
});
|
|
||||||
|
|
||||||
// First remove top folder in Github source zip, temp1 -> temp2
|
|
||||||
removeTopFolder(temp1, temp2);
|
|
||||||
|
|
||||||
// Then sign the zip
|
|
||||||
ZipUtils.signZip(temp2, mFile);
|
|
||||||
|
|
||||||
// Delete temp files
|
|
||||||
temp1.delete();
|
|
||||||
temp2.delete();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
BaseActivity activity = getActivity();
|
|
||||||
if (activity == null) return;
|
|
||||||
progressDialog.dismiss();
|
|
||||||
if (result) {
|
|
||||||
Uri uri = Uri.fromFile(mFile);
|
|
||||||
if (mInstall) {
|
|
||||||
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class));
|
|
||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
} else {
|
|
||||||
SnackbarMaker.showUri(activity, uri);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Utils.toast(R.string.process_error, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
super.onPostExecute(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exec(Void... voids) {
|
|
||||||
getActivity().runWithPermission(
|
|
||||||
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, super::exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ProgressInputStream extends FilterInputStream {
|
|
||||||
|
|
||||||
ProgressInputStream(InputStream in) {
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDlProgress(int step) {
|
|
||||||
progress += step;
|
|
||||||
progressDialog.setMessage(getActivity().getString(R.string.zip_download_msg,
|
|
||||||
(int) (100 * (double) progress / total + 0.5)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized int read() throws IOException {
|
|
||||||
int b = super.read();
|
|
||||||
if (b > 0) {
|
|
||||||
Data.mainHandler.post(() -> updateDlProgress(1));
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(@NonNull byte[] b) throws IOException {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
|
|
||||||
int read = super.read(b, off, len);
|
|
||||||
if (read > 0) {
|
|
||||||
Data.mainHandler.post(() -> updateDlProgress(read));
|
|
||||||
}
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -55,6 +55,13 @@ public class NotificationProgress implements DownloadProgressListener {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dlFail() {
|
||||||
|
builder.setProgress(0, 0, false)
|
||||||
|
.setContentText(Data.MM().getString(R.string.download_file_error))
|
||||||
|
.setSmallIcon(R.drawable.ic_cancel);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
public void dismiss() {
|
public void dismiss() {
|
||||||
mgr.cancel(Const.ID.DOWNLOAD_PROGRESS_ID);
|
mgr.cancel(Const.ID.DOWNLOAD_PROGRESS_ID);
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
<string name="update_channel">Magisk Updates</string>
|
<string name="update_channel">Magisk Updates</string>
|
||||||
<string name="progress_channel">Progress Notifications</string>
|
<string name="progress_channel">Progress Notifications</string>
|
||||||
<string name="download_complete">Download complete</string>
|
<string name="download_complete">Download complete</string>
|
||||||
|
<string name="download_file_error">Error downloading file</string>
|
||||||
<string name="magisk_update_title">New Magisk Update Available!</string>
|
<string name="magisk_update_title">New Magisk Update Available!</string>
|
||||||
<string name="manager_update_title">New Magisk Manager Update Available!</string>
|
<string name="manager_update_title">New Magisk Manager Update Available!</string>
|
||||||
|
|
||||||
@ -123,7 +124,6 @@
|
|||||||
<string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now?</string>
|
<string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now?</string>
|
||||||
<string name="setup_title">Additional Setup</string>
|
<string name="setup_title">Additional Setup</string>
|
||||||
<string name="setup_msg">Running environment setup…</string>
|
<string name="setup_msg">Running environment setup…</string>
|
||||||
<string name="download_file_error">Error downloading file</string>
|
|
||||||
<string name="downloading_toast">Downloading %1$s</string>
|
<string name="downloading_toast">Downloading %1$s</string>
|
||||||
<string name="no_rw_storage">This feature will not work without permission to write external storage.</string>
|
<string name="no_rw_storage">This feature will not work without permission to write external storage.</string>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user