diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7c0114162..7b020b563 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,7 @@
-
+
@@ -22,8 +21,7 @@
-
+ android:exported="true" />
+
-
+ android:theme="@style/AppTheme.Transparent" />
+
+
-
-
+
-
-
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
-
+
\ No newline at end of file
diff --git a/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java b/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java
new file mode 100644
index 000000000..b179f40dd
--- /dev/null
+++ b/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java
@@ -0,0 +1,98 @@
+package com.topjohnwu.magisk;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.topjohnwu.magisk.asyncs.FlashZip;
+import com.topjohnwu.magisk.components.Activity;
+import com.topjohnwu.magisk.utils.AdaptiveList;
+import com.topjohnwu.magisk.utils.Shell;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+
+public class FlashActivity extends Activity {
+
+ @BindView(R.id.toolbar) Toolbar toolbar;
+ @BindView(R.id.flash_logs) RecyclerView flashLogs;
+ @BindView(R.id.button_panel) LinearLayout buttonPanel;
+
+ private AdaptiveList rootShellOutput;
+
+ @OnClick(R.id.no_thanks)
+ public void dismiss() {
+ finish();
+ }
+
+ @OnClick(R.id.reboot)
+ public void reboot() {
+ Shell.getRootShell(this).su_raw("reboot");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_flash);
+ ButterKnife.bind(this);
+ rootShellOutput = new AdaptiveList<>(flashLogs);
+ setSupportActionBar(toolbar);
+ ActionBar ab = getSupportActionBar();
+ if (ab != null) {
+ ab.setTitle(R.string.flashing);
+ }
+ setFloating();
+
+ flashLogs.setAdapter(new FlashLogAdapter());
+
+ // We must receive a Uri of the target zip
+ Uri uri = getIntent().getData();
+
+ new FlashZip(this, uri, rootShellOutput)
+ .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE))
+ .exec();
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Prevent user accidentally press back button
+ }
+
+ private class FlashLogAdapter extends RecyclerView.Adapter {
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.list_item_flashlog, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ holder.text.setText(rootShellOutput.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return rootShellOutput.size();
+ }
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+
+ @BindView(R.id.textView) TextView text;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ }
+ }
+}
diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
index 77568ffb4..cbfb557be 100644
--- a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
+++ b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
@@ -26,7 +26,7 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
-import com.topjohnwu.magisk.asyncs.ProcessMagiskZip;
+import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
@@ -144,7 +144,13 @@ public class MagiskFragment extends Fragment
@Override
public void onDownloadDone(Uri uri, Context context) {
- new ProcessMagiskZip(getActivity(), uri, boot, enc, verity).exec();
+ if (Shell.rootAccess()) {
+ new SetInstallFlags(boot, enc, verity)
+ .setCallBack(() -> startActivity(new Intent(context, FlashActivity.class).setData(uri)))
+ .exec(context);
+ } else {
+ Utils.showUriSnack(getActivity(), uri);
+ }
}
},
magiskManager.magiskLink,
@@ -160,6 +166,28 @@ public class MagiskFragment extends Fragment
.show();
}
+ private static class SetInstallFlags extends ParallelTask {
+
+ private String boot;
+ private boolean enc, verity;
+
+ SetInstallFlags(String boot, boolean enc, boolean verity) {
+ this.boot = boot;
+ this.enc = enc;
+ this.verity = verity;
+ }
+
+ @Override
+ protected Void doInBackground(Context... contexts) {
+ Shell.getRootShell(contexts[0]).su_raw("rm -f /dev/.magisk",
+ (boot != null) ? "echo \"BOOTIMAGE=" + boot + "\" >> /dev/.magisk" : "",
+ "echo \"KEEPFORCEENCRYPT=" + String.valueOf(enc) + "\" >> /dev/.magisk",
+ "echo \"KEEPVERITY=" + String.valueOf(verity) + "\" >> /dev/.magisk"
+ );
+ return null;
+ }
+ }
+
@OnClick(R.id.uninstall_button)
public void uninstall() {
new AlertDialogBuilder(getActivity())
diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java
index 34907db8d..758a05e5a 100644
--- a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java
+++ b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk;
import android.app.Activity;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
@@ -14,13 +13,11 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
-import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger;
-import com.topjohnwu.magisk.utils.Shell;
import java.util.ArrayList;
import java.util.List;
@@ -87,8 +84,9 @@ public class ModulesFragment extends Fragment implements CallbackEvent.Listener<
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
- final Uri uri = data.getData();
- new FlashZip(getActivity(), uri).exec();
+ Intent intent = new Intent(getActivity(), FlashActivity.class);
+ intent.setData(data.getData()).putExtra("ACTION", "flash");
+ startActivity(intent);
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java
index 8f1726fb9..8e2a42ee7 100644
--- a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java
+++ b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java
@@ -162,14 +162,14 @@ public class SettingsActivity extends Activity {
new AlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisksu_title)
.setMessage(R.string.no_magisksu_msg)
- .setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide().enable())
+ .setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide(getActivity()).enable())
.setCancelable(false)
.show();
} else {
- new MagiskHide().enable();
+ new MagiskHide(getActivity()).enable();
}
} else {
- new MagiskHide().disable();
+ new MagiskHide(getActivity()).disable();
}
break;
case "hosts":
diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java
index 90dcfe778..228e7f6e7 100644
--- a/app/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java
+++ b/app/src/main/java/com/topjohnwu/magisk/adapters/ApplicationAdapter.java
@@ -1,5 +1,6 @@
package com.topjohnwu.magisk.adapters;
+import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.design.widget.Snackbar;
@@ -86,10 +87,10 @@ public class ApplicationAdapter extends RecyclerView.Adapter {
if (isChecked) {
- new MagiskHide().add(info.packageName);
+ new MagiskHide((Activity) holder.itemView.getContext()).add(info.packageName);
mHideList.add(info.packageName);
} else {
- new MagiskHide().rm(info.packageName);
+ new MagiskHide((Activity) holder.itemView.getContext()).rm(info.packageName);
mHideList.remove(info.packageName);
}
});
diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java
index c7fa300ff..df9f48aa2 100644
--- a/app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java
+++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java
@@ -1,14 +1,12 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.net.Uri;
-import android.widget.Toast;
+import android.text.TextUtils;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
-import com.topjohnwu.magisk.components.AlertDialogBuilder;
-import com.topjohnwu.magisk.utils.Logger;
+import com.topjohnwu.magisk.utils.AdaptiveList;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
@@ -26,11 +24,12 @@ public class FlashZip extends ParallelTask {
private File mCachedFile, mScriptFile, mCheckFile;
private String mFilename;
- private ProgressDialog progress;
+ private AdaptiveList mList;
- public FlashZip(Activity context, Uri uri) {
+ public FlashZip(Activity context, Uri uri, AdaptiveList list) {
super(context);
mUri = uri;
+ mList = list;
mCachedFile = new File(magiskManager.getCacheDir(), "install.zip");
mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary");
@@ -40,97 +39,77 @@ public class FlashZip extends ParallelTask {
mFilename = Utils.getNameFromUri(magiskManager, mUri);
}
- private void copyToCache() throws Throwable {
- publishProgress(magiskManager.getString(R.string.copying_msg));
+ private void copyToCache() throws Exception {
+ mList.add(magiskManager.getString(R.string.copying_msg));
- if (mCachedFile.exists() && !mCachedFile.delete()) {
- Logger.error("FlashZip: Error while deleting already existing file");
- throw new IOException();
- }
+ mCachedFile.delete();
try (
- InputStream in = magiskManager.getContentResolver().openInputStream(mUri);
- OutputStream outputStream = new FileOutputStream(mCachedFile)
+ InputStream in = magiskManager.getContentResolver().openInputStream(mUri);
+ OutputStream outputStream = new FileOutputStream(mCachedFile)
) {
byte buffer[] = new byte[1024];
int length;
if (in == null) throw new FileNotFoundException();
while ((length = in.read(buffer)) > 0)
outputStream.write(buffer, 0, length);
-
- Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
} catch (FileNotFoundException e) {
- Logger.error("FlashZip: Invalid Uri");
+ mList.add("! Invalid Uri");
throw e;
} catch (IOException e) {
- Logger.error("FlashZip: Error in creating file");
+ mList.add("! Cannot copy to cache");
throw e;
}
}
private boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
- List ret;
- ret = Utils.readFile(magiskManager.rootShell, mCheckFile.getPath());
+ List ret = Utils.readFile(magiskManager.rootShell, mCheckFile.getPath());
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
}
- private int cleanup(int ret) {
- magiskManager.rootShell.su_raw(
- "rm -rf " + mCachedFile.getParent() + "/*",
- "rm -rf " + MagiskManager.TMP_FOLDER_PATH
- );
- return ret;
- }
-
@Override
protected void onPreExecute() {
- progress = new ProgressDialog(activity);
- progress.setTitle(R.string.zip_install_progress_title);
- progress.show();
+ // UI updates must run in the UI thread
+ mList.setCallback(this::publishProgress);
}
@Override
protected void onProgressUpdate(String... values) {
- progress.setMessage(values[0]);
+ mList.updateView();
}
@Override
protected Integer doInBackground(Void... voids) {
- Logger.dev("FlashZip Running... " + mFilename);
- List ret;
try {
copyToCache();
- if (!unzipAndCheck()) return cleanup(0);
- publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
- ret = magiskManager.rootShell.su(
- "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile,
- "if [ $? -eq 0 ]; then echo true; else echo false; fi"
+ if (!unzipAndCheck()) return 0;
+ mList.add(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
+ magiskManager.rootShell.su(mList,
+ "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile +
+ " && echo 'Success!' || echo 'Failed!'"
);
- if (!Utils.isValidShellResponse(ret)) return -1;
- Logger.dev("FlashZip: Console log:");
- for (String line : ret) {
- Logger.dev(line);
- }
- if (Boolean.parseBoolean(ret.get(ret.size() - 1)))
- return cleanup(1);
-
- } catch (Throwable e) {
+ if (TextUtils.equals(mList.get(mList.size() - 1), "Success!"))
+ return 1;
+ } catch (Exception e) {
e.printStackTrace();
}
- return cleanup(-1);
+ return -1;
}
// -1 = error, manual install; 0 = invalid zip; 1 = success
@Override
protected void onPostExecute(Integer result) {
- progress.dismiss();
+ magiskManager.rootShell.su_raw(
+ "rm -rf " + mCachedFile.getParent() + "/*",
+ "rm -rf " + MagiskManager.TMP_FOLDER_PATH
+ );
switch (result) {
case -1:
- Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show();
+ mList.add(magiskManager.getString(R.string.install_error));
Utils.showUriSnack(activity, mUri);
break;
case 0:
- Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
+ mList.add(magiskManager.getString(R.string.invalid_zip));
break;
case 1:
onSuccess();
@@ -140,14 +119,6 @@ public class FlashZip extends ParallelTask {
}
protected void onSuccess() {
- magiskManager.updateCheckDone.trigger();
new LoadModules(activity).exec();
-
- new AlertDialogBuilder(activity)
- .setTitle(R.string.reboot_title)
- .setMessage(R.string.reboot_msg)
- .setPositiveButton(R.string.reboot, (dialogInterface, i) -> magiskManager.rootShell.su_raw("reboot"))
- .setNegativeButton(R.string.no_thanks, null)
- .show();
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java
index fea8d36eb..b0fdbe712 100644
--- a/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java
+++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java
@@ -8,8 +8,6 @@ public class MagiskHide extends ParallelTask