Massive Zip flashing refactoring

This commit is contained in:
topjohnwu 2017-02-15 05:24:02 +08:00
parent f9ab060403
commit 23c84a7803
20 changed files with 388 additions and 222 deletions

View File

@ -18,8 +18,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.components.AboutCardRow; import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -86,7 +86,7 @@ public class AboutActivity extends Activity {
result = Html.fromHtml(changes); result = Html.fromHtml(changes);
} }
appChangelog.setOnClickListener(v -> { appChangelog.setOnClickListener(v -> {
AlertDialog d = Utils.getAlertDialogBuilder(this) AlertDialog d = new AlertDialogBuilder(this)
.setTitle(R.string.app_changelog) .setTitle(R.string.app_changelog)
.setMessage(result) .setMessage(result)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
@ -105,7 +105,7 @@ public class AboutActivity extends Activity {
} else { } else {
result = Html.fromHtml(getString(R.string.app_developers_)); result = Html.fromHtml(getString(R.string.app_developers_));
} }
AlertDialog d = Utils.getAlertDialogBuilder(this) AlertDialog d = new AlertDialogBuilder(this)
.setTitle(R.string.app_developers) .setTitle(R.string.app_developers)
.setMessage(result) .setMessage(result)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)

View File

@ -16,8 +16,10 @@ import android.widget.CheckBox;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.ProcessMagiskZip;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.receivers.MagiskDlReceiver; import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.CallbackEvent; import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@ -67,13 +69,22 @@ public class InstallFragment extends Fragment implements CallbackEvent.Listener<
bootImage = getApplication().blockList.get(spinner.getSelectedItemPosition()); bootImage = getApplication().blockList.get(spinner.getSelectedItemPosition());
} }
String filename = "Magisk-v" + getApplication().remoteMagiskVersion + ".zip"; String filename = "Magisk-v" + getApplication().remoteMagiskVersion + ".zip";
Utils.getAlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())
.setTitle(getString(R.string.repo_install_title, getString(R.string.magisk))) .setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
.setMessage(getString(R.string.repo_install_msg, filename)) .setMessage(getString(R.string.repo_install_msg, filename))
.setCancelable(true) .setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive( .setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
getActivity(), getActivity(),
new MagiskDlReceiver(bootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()), new DownloadReceiver() {
private String boot = bootImage;
private boolean enc = keepEncChkbox.isChecked();
private boolean verity = keepVerityChkbox.isChecked();
@Override
public void onDownloadDone(Uri uri) {
new ProcessMagiskZip(getActivity(), uri, boot, enc, verity).exec();
}
},
getApplication().magiskLink, getApplication().magiskLink,
Utils.getLegalFilename(filename))) Utils.getLegalFilename(filename)))
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> { .setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
@ -86,7 +97,7 @@ public class InstallFragment extends Fragment implements CallbackEvent.Listener<
uninstallButton.setVisibility(View.GONE); uninstallButton.setVisibility(View.GONE);
} else { } else {
uninstallButton.setOnClickListener(vi -> { uninstallButton.setOnClickListener(vi -> {
Utils.getAlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())
.setTitle("Uninstall Magisk") .setTitle("Uninstall Magisk")
.setMessage("This will remove all modules, MagiskSU, and potentially re-encrypt your device\nAre you sure to process?") .setMessage("This will remove all modules, MagiskSU, and potentially re-encrypt your device\nAre you sure to process?")
.setPositiveButton(R.string.yes, (dialogInterface, i) -> { .setPositiveButton(R.string.yes, (dialogInterface, i) -> {

View File

@ -26,6 +26,7 @@ import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.SerialTask; import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@ -121,7 +122,7 @@ public class MagiskLogFragment extends Fragment {
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500); new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
} }
} else { } else {
Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show(); SnackbarMaker.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
} }
} }
} }
@ -150,7 +151,7 @@ public class MagiskLogFragment extends Fragment {
case 1: case 1:
Shell.su("echo > " + MAGISK_LOG); Shell.su("echo > " + MAGISK_LOG);
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return ""; return "";
case 2: case 2:

View File

@ -14,7 +14,7 @@ import android.widget.TextView;
import com.github.clans.fab.FloatingActionButton; import com.github.clans.fab.FloatingActionButton;
import com.topjohnwu.magisk.adapters.ModulesAdapter; import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.asyncs.FlashZIP; import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.LoadModules; import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.module.Module;
@ -87,7 +87,7 @@ public class ModulesFragment extends Fragment implements CallbackEvent.Listener<
if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) { if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file // Get the URI of the selected file
final Uri uri = data.getData(); final Uri uri = data.getData();
new FlashZIP(getActivity(), uri).exec(); new FlashZip(getActivity(), uri).exec();
} }
} }

View File

@ -16,6 +16,7 @@ import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.MagiskHide; import com.topjohnwu.magisk.asyncs.MagiskHide;
import com.topjohnwu.magisk.asyncs.SerialTask; import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@ -188,7 +189,7 @@ public class SettingsActivity extends Activity {
enabled = prefs.getBoolean("magiskhide", false); enabled = prefs.getBoolean("magiskhide", false);
if (enabled) { if (enabled) {
if (!getApplication().isSuClient) { if (!getApplication().isSuClient) {
Utils.getAlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisksu_title) .setTitle(R.string.no_magisksu_title)
.setMessage(R.string.no_magisksu_msg) .setMessage(R.string.no_magisksu_msg)
.setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide().enable()) .setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide().enable())

View File

@ -15,6 +15,7 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.CheckUpdates; import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.CallbackEvent; import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
@ -97,7 +98,7 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
if (getApplication().magiskVersion < 0 && Shell.rootAccess() && !noDialog) { if (getApplication().magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
noDialog = true; noDialog = true;
Utils.getAlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisk_title) .setTitle(R.string.no_magisk_title)
.setMessage(R.string.no_magisk_msg) .setMessage(R.string.no_magisk_msg)
.setCancelable(true) .setCancelable(true)
@ -229,7 +230,7 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
magiskCheckUpdatesProgress.setVisibility(View.GONE); magiskCheckUpdatesProgress.setVisibility(View.GONE);
mSwipeRefreshLayout.setRefreshing(false); mSwipeRefreshLayout.setRefreshing(false);
updateMagisk = Utils.getAlertDialogBuilder(getActivity()) updateMagisk = new AlertDialogBuilder(getActivity())
.setTitle(R.string.magisk_update_title) .setTitle(R.string.magisk_update_title)
.setMessage(getString(R.string.magisk_update_message, getApplication().remoteMagiskVersion)) .setMessage(getString(R.string.magisk_update_message, getApplication().remoteMagiskVersion))
.setCancelable(true) .setCancelable(true)

View File

@ -14,6 +14,7 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MagiskHide; import com.topjohnwu.magisk.asyncs.MagiskHide;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
@ -76,11 +77,10 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
if (SNLIST.contains(info.packageName)) { if (SNLIST.contains(info.packageName)) {
holder.checkBox.setChecked(true); holder.checkBox.setChecked(true);
holder.checkBox.setEnabled(false); holder.checkBox.setEnabled(false);
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v ->
Snackbar snackbar = Snackbar.make(holder.itemView, R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG); SnackbarMaker.make(holder.itemView,
((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setMaxLines(2); R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).show()
snackbar.show(); );
});
} else { } else {
holder.checkBox.setEnabled(true); holder.checkBox.setEnabled(true);
holder.checkBox.setChecked(mHideList.contains(info.packageName)); holder.checkBox.setChecked(mHideList.contains(info.packageName));

View File

@ -13,6 +13,7 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.SerialTask; import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
@ -66,7 +67,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created; int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
} }
}.exec()); }.exec());
@ -86,7 +87,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created; int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module); updateDeleteButton(holder, module);
} }
}.exec()); }.exec());

View File

@ -15,9 +15,10 @@ import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.superuser.Policy; import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.utils.Utils;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -70,7 +71,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY; policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString( String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName); isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy); dbHelper.addPolicy(policy);
} }
}); });
@ -80,7 +81,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
policy.notification = isChecked; policy.notification = isChecked;
String message = v.getContext().getString( String message = v.getContext().getString(
isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName); isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName);
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy); dbHelper.addPolicy(policy);
} }
}); });
@ -90,18 +91,18 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
policy.logging = isChecked; policy.logging = isChecked;
String message = v.getContext().getString( String message = v.getContext().getString(
isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName); isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName);
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy); dbHelper.addPolicy(policy);
} }
}); });
holder.delete.setOnClickListener(v -> Utils.getAlertDialogBuilder(v.getContext()) holder.delete.setOnClickListener(v -> new AlertDialogBuilder(v.getContext())
.setTitle(R.string.su_revoke_title) .setTitle(R.string.su_revoke_title)
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName)) .setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
.setPositiveButton(R.string.yes, (dialog, which) -> { .setPositiveButton(R.string.yes, (dialog, which) -> {
policyList.remove(position); policyList.remove(position);
notifyItemRemoved(position); notifyItemRemoved(position);
notifyItemRangeChanged(position, policyList.size()); notifyItemRangeChanged(position, policyList.size());
Snackbar.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName), SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
Snackbar.LENGTH_SHORT).show(); Snackbar.LENGTH_SHORT).show();
dbHelper.deletePolicy(policy.uid); dbHelper.deletePolicy(policy.uid);
}) })

View File

@ -17,8 +17,10 @@ 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.ProcessRepoZip;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.module.Repo; import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.receivers.RepoDlReceiver; import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebWindow; import com.topjohnwu.magisk.utils.WebWindow;
@ -75,13 +77,18 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
}); });
holder.updateImage.setOnClickListener(view -> { holder.updateImage.setOnClickListener(view -> {
String filename = repo.getName() + "-" + repo.getVersion() + ".zip"; String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
Utils.getAlertDialogBuilder(context) new AlertDialogBuilder(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, filename)) .setMessage(context.getString(R.string.repo_install_msg, filename))
.setCancelable(true) .setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive( .setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
context, context,
new RepoDlReceiver(), new DownloadReceiver() {
@Override
public void onDownloadDone(Uri uri) {
new ProcessRepoZip(activity, uri).exec();
}
},
repo.getZipUrl(), repo.getZipUrl(),
Utils.getLegalFilename(filename))) Utils.getLegalFilename(filename)))
.setNegativeButton(R.string.no_thanks, null) .setNegativeButton(R.string.no_thanks, null)

View File

@ -2,13 +2,12 @@ package com.topjohnwu.magisk.asyncs;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.OpenableColumns;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@ -22,46 +21,29 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
public class FlashZIP extends SerialTask<Void, String, Integer> { public class FlashZip extends SerialTask<Void, String, Integer> {
protected Uri mUri; private Uri mUri;
protected File mCachedFile; private File mCachedFile, mScriptFile, mCheckFile;
private String mFilename; private String mFilename;
private ProgressDialog progress; private ProgressDialog progress;
public FlashZIP(Activity context, Uri uri, String filename) { public FlashZip(Activity context, Uri uri) {
super(context); super(context);
mUri = uri; mUri = uri;
mFilename = filename;
}
public FlashZIP(Activity context, Uri uri) { mCachedFile = new File(magiskManager.getCacheDir(), "install.zip");
super(context); mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary");
mUri = uri; mCheckFile = new File(mScriptFile.getParent(), "updater-script");
// Try to get the filename ourselves // Try to get the filename ourselves
Cursor c = magiskManager.getContentResolver().query(uri, null, null, null, null); mFilename = Utils.getNameFromUri(magiskManager, mUri);
if (c != null) {
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
c.moveToFirst();
if (nameIndex != -1) {
mFilename = c.getString(nameIndex);
}
c.close();
}
if (mFilename == null) {
int idx = uri.getPath().lastIndexOf('/');
mFilename = uri.getPath().substring(idx + 1);
}
} }
protected void preProcessing() throws Throwable { private void copyToCache() throws Throwable {
}
protected void copyToCache() throws Throwable {
publishProgress(magiskManager.getString(R.string.copying_msg)); publishProgress(magiskManager.getString(R.string.copying_msg));
mCachedFile = new File(magiskManager.getCacheDir().getAbsolutePath() + "/install.zip");
if (mCachedFile.exists() && !mCachedFile.delete()) { if (mCachedFile.exists() && !mCachedFile.delete()) {
Logger.error("FlashZip: Error while deleting already existing file"); Logger.error("FlashZip: Error while deleting already existing file");
throw new IOException(); throw new IOException();
@ -73,9 +55,9 @@ public class FlashZIP extends SerialTask<Void, String, Integer> {
byte buffer[] = new byte[1024]; byte buffer[] = new byte[1024];
int length; int length;
if (in == null) throw new FileNotFoundException(); if (in == null) throw new FileNotFoundException();
while ((length = in.read(buffer)) > 0) { while ((length = in.read(buffer)) > 0)
outputStream.write(buffer, 0, length); outputStream.write(buffer, 0, length);
}
Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath()); Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Logger.error("FlashZip: Invalid Uri"); Logger.error("FlashZip: Invalid Uri");
@ -86,13 +68,21 @@ public class FlashZIP extends SerialTask<Void, String, Integer> {
} }
} }
protected boolean unzipAndCheck() { protected boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android"); ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
List<String> ret; List<String> ret;
ret = Utils.readFile(mCachedFile.getParent() + "/META-INF/com/google/android/updater-script"); ret = Utils.readFile(mCheckFile.getPath());
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK"); return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
} }
private int cleanup(int ret) {
Shell.su(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + MagiskManager.TMP_FOLDER_PATH
);
return ret;
}
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
progress = new ProgressDialog(activity); progress = new ProgressDialog(activity);
@ -110,17 +100,11 @@ public class FlashZIP extends SerialTask<Void, String, Integer> {
Logger.dev("FlashZip Running... " + mFilename); Logger.dev("FlashZip Running... " + mFilename);
List<String> ret; List<String> ret;
try { try {
preProcessing();
copyToCache(); copyToCache();
} catch (Throwable e) { if (!unzipAndCheck()) return cleanup(0);
e.printStackTrace();
return -1;
}
if (!unzipAndCheck()) return 0;
publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename)); publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
ret = Shell.su( ret = Shell.su(
"BOOTMODE=true sh " + mCachedFile.getParent() + "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile,
"/META-INF/com/google/android/update-binary dummy 1 " + mCachedFile.getPath(),
"if [ $? -eq 0 ]; then echo true; else echo false; fi" "if [ $? -eq 0 ]; then echo true; else echo false; fi"
); );
if (!Utils.isValidShellResponse(ret)) return -1; if (!Utils.isValidShellResponse(ret)) return -1;
@ -128,14 +112,13 @@ public class FlashZIP extends SerialTask<Void, String, Integer> {
for (String line : ret) { for (String line : ret) {
Logger.dev(line); Logger.dev(line);
} }
Shell.su( if (Boolean.parseBoolean(ret.get(ret.size() - 1)))
"rm -rf " + mCachedFile.getParent() + "/*", return cleanup(1);
"rm -rf " + MagiskManager.TMP_FOLDER_PATH
); } catch (Throwable e) {
if (Boolean.parseBoolean(ret.get(ret.size() - 1))) { e.printStackTrace();
return 1;
} }
return -1; return cleanup(-1);
} }
// -1 = error, manual install; 0 = invalid zip; 1 = success // -1 = error, manual install; 0 = invalid zip; 1 = success
@ -146,8 +129,7 @@ public class FlashZIP extends SerialTask<Void, String, Integer> {
switch (result) { switch (result) {
case -1: case -1:
Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show(); Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show();
Toast.makeText(magiskManager, magiskManager.getString(R.string.manual_install_1, mUri.getPath()), Toast.LENGTH_LONG).show(); Utils.showUriSnack(activity, mUri);
Toast.makeText(magiskManager, magiskManager.getString(R.string.manual_install_2), Toast.LENGTH_LONG).show();
break; break;
case 0: case 0:
Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show(); Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
@ -162,7 +144,7 @@ public class FlashZIP extends SerialTask<Void, String, Integer> {
magiskManager.updateCheckDone.trigger(); magiskManager.updateCheckDone.trigger();
new LoadModules(activity).exec(); new LoadModules(activity).exec();
Utils.getAlertDialogBuilder(activity) new AlertDialogBuilder(activity)
.setTitle(R.string.reboot_title) .setTitle(R.string.reboot_title)
.setMessage(R.string.reboot_msg) .setMessage(R.string.reboot_msg)
.setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot")) .setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot"))

View File

@ -0,0 +1,93 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File;
public class ProcessMagiskZip extends ParallelTask<Void, Void, Boolean> {
private Uri mUri;
private ProgressDialog progressDialog;
private String mBoot;
private boolean mEnc, mVerity;
public ProcessMagiskZip(Activity context, Uri uri, String boot, boolean enc, boolean verity) {
super(context);
mUri = uri;
mBoot = boot;
mEnc = enc;
mVerity = verity;
}
@Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(activity,
activity.getString(R.string.zip_install_progress_title),
activity.getString(R.string.zip_install_unzip_zip_msg));
}
@Override
protected Boolean doInBackground(Void... params) {
if (Shell.rootAccess()) {
try {
// We might not have busybox yet, unzip with Java
// We shall have complete busybox after Magisk installation
File tempdir = new File(magiskManager.getCacheDir(), "magisk");
ZipUtils.unzip(magiskManager.getContentResolver().openInputStream(mUri), tempdir);
// Running in parallel mode, open new shell
Shell.su(true,
"echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" > /dev/.magisk",
"echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
"echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk",
"mkdir -p " + MagiskManager.TMP_FOLDER_PATH,
"cp -af " + tempdir + "/. " + MagiskManager.TMP_FOLDER_PATH + "/magisk",
"mv -f " + tempdir + "/META-INF " + magiskManager.getCacheDir() + "/META-INF",
"rm -rf " + tempdir
);
} catch (Exception e) {
Logger.error("ProcessMagiskZip: Error!");
e.printStackTrace();
return false;
}
return true;
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result)
new FlashZip(activity, mUri) {
@Override
protected boolean unzipAndCheck() throws Exception {
// Don't need to check, as it is downloaded in correct form
return true;
}
@Override
protected void onSuccess() {
new SerialTask<Void, Void, Void>(activity) {
@Override
protected Void doInBackground(Void... params) {
Shell.su("setprop magisk.version "
+ String.valueOf(magiskManager.remoteMagiskVersion));
magiskManager.updateCheckDone.trigger();
return null;
}
}.exec();
super.onSuccess();
}
}.exec();
else
Utils.showUriSnack(activity, mUri);
}
}

View File

@ -0,0 +1,77 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import android.widget.Toast;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.ByteArrayInOutStream;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.OutputStream;
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
private Uri mUri;
private ProgressDialog progressDialog;
public ProcessRepoZip(Activity context, Uri uri) {
super(context);
mUri = uri;
}
@Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(activity,
activity.getString(R.string.zip_install_progress_title),
activity.getString(R.string.zip_install_process_zip_msg));
}
@Override
protected Boolean doInBackground(Void... params) {
// Create a buffer in memory for input/output
ByteArrayInOutStream buffer = new ByteArrayInOutStream();
try {
// First remove top folder (the folder with the repo name) in Github source zip
ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), buffer);
// Then sign the zip for the first time
ZipUtils.signZip(activity, buffer.getInputStream(), buffer, false);
// Adjust the zip to prevent unzip issues
ZipUtils.adjustZip(buffer);
// Finally, sign the whole zip file again
ZipUtils.signZip(activity, buffer.getInputStream(), buffer, true);
// Write it back to the downloaded zip
try (OutputStream out = activity.getContentResolver().openOutputStream(mUri)) {
buffer.writeTo(out);
}
return true;
} catch (Exception e) {
Logger.error("ProcessRepoZip: Error!");
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
if (Shell.rootAccess())
new FlashZip(activity, mUri).exec();
else
Utils.showUriSnack(activity, mUri);
} else {
Toast.makeText(activity, R.string.process_error, Toast.LENGTH_LONG).show();
}
}
}

View File

@ -0,0 +1,41 @@
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import butterknife.ButterKnife;
public class SnackbarMaker {
public static Snackbar make(Activity activity, CharSequence text, int duration) {
View view = activity.findViewById(android.R.id.content);
return make(view, text, duration);
}
public static Snackbar make(Activity activity, @StringRes int resId, int duration) {
return make(activity, activity.getString(resId), duration);
}
public static Snackbar make(View view, CharSequence text, int duration) {
Snackbar snack = Snackbar.make(view, text, duration);
setup(snack);
return snack;
}
public static Snackbar make(View view, @StringRes int resId, int duration) {
Snackbar snack = Snackbar.make(view, resId, duration);
setup(snack);
return snack;
}
private static void setup(Snackbar snack) {
TextView text = ButterKnife.findById(snack.getView(), android.support.design.R.id.snackbar_text);
text.setMaxLines(2);
text.setEllipsize(TextUtils.TruncateAt.START);
}
}

View File

@ -1,68 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.net.Uri;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.FlashZIP;
import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File;
public class MagiskDlReceiver extends DownloadReceiver {
String mBoot;
boolean mEnc, mVerity;
public MagiskDlReceiver(String bootImage, boolean keepEnc, boolean keepVerity) {
mBoot = bootImage;
mEnc = keepEnc;
mVerity = keepVerity;
}
@Override
public void onDownloadDone(Uri uri) {
new FlashZIP(activity, uri, mFilename) {
@Override
protected void preProcessing() throws Throwable {
Shell.su(
"echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" > /dev/.magisk",
"echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
"echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk"
);
}
@Override
protected boolean unzipAndCheck() {
publishProgress(activity.getString(R.string.zip_install_unzip_zip_msg));
if (Shell.rootAccess()) {
// We might not have busybox yet, unzip with Java
// We will have complete busybox after Magisk installation
ZipUtils.unzip(mCachedFile, new File(mCachedFile.getParent(), "magisk"));
Shell.su(
"mkdir -p " + MagiskManager.TMP_FOLDER_PATH + "/magisk",
"cp -af " + mCachedFile.getParent() + "/magisk/. " + MagiskManager.TMP_FOLDER_PATH + "/magisk",
"mv -f " + mCachedFile.getParent() + "/magisk/META-INF " + mCachedFile.getParent() + "/META-INF"
);
}
return true;
}
@Override
protected void onSuccess() {
new SerialTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
Shell.su("setprop magisk.version "
+ String.valueOf(((MagiskManager) activity.getApplicationContext()).remoteMagiskVersion));
return null;
}
}.exec();
super.onSuccess();
}
}.exec();
}
}

View File

@ -1,42 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.net.Uri;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.FlashZIP;
import com.topjohnwu.magisk.utils.ByteArrayInOutStream;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.OutputStream;
public class RepoDlReceiver extends DownloadReceiver {
@Override
public void onDownloadDone(Uri uri) {
// Flash the zip
new FlashZIP(activity, uri, mFilename){
@Override
protected void preProcessing() throws Throwable {
// Process and sign the zip
publishProgress(activity.getString(R.string.zip_install_process_zip_msg));
ByteArrayInOutStream buffer = new ByteArrayInOutStream();
// First remove top folder (the folder with the repo name) in Github source zip
ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), buffer);
// Then sign the zip for the first time
ZipUtils.signZip(activity, buffer.getInputStream(), buffer, false);
// Adjust the zip to prevent unzip issues
ZipUtils.adjustZip(buffer);
// Finally, sign the whole zip file again
ZipUtils.signZip(activity, buffer.getInputStream(), buffer, true);
// Write it back to the downloaded zip
try (OutputStream out = activity.getContentResolver().openOutputStream(mUri)) {
buffer.writeTo(out);
}
}
}.exec();
}
}

View File

@ -7,17 +7,19 @@ import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.OpenableColumns;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils; import android.text.TextUtils;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.LoadRepos; import com.topjohnwu.magisk.asyncs.LoadRepos;
import com.topjohnwu.magisk.components.AlertDialogBuilder; import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.database.RepoDatabaseHelper; import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.receivers.DownloadReceiver; import com.topjohnwu.magisk.receivers.DownloadReceiver;
@ -117,15 +119,6 @@ public class Utils {
return null; return null;
} }
public static AlertDialog.Builder getAlertDialogBuilder(Context context) {
// if (((MagiskManager) context.getApplicationContext()).isDarkTheme) {
// return new AlertDialog.Builder(context, R.style.AlertDialog_dh);
// } else {
// return new AlertDialog.Builder(context);
// }
return new AlertDialogBuilder(context);
}
public static boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) { public static boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch); return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
} }
@ -164,4 +157,29 @@ public class Utils {
new RepoDatabaseHelper(activity).clearRepo(); new RepoDatabaseHelper(activity).clearRepo();
Toast.makeText(activity, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show();
} }
public static String getNameFromUri(Context context, Uri uri) {
String name = null;
try (Cursor c = context.getContentResolver().query(uri, null, null, null, null)) {
if (c != null) {
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex != -1) {
c.moveToFirst();
name = c.getString(nameIndex);
}
}
}
if (name == null) {
int idx = uri.getPath().lastIndexOf('/');
name = uri.getPath().substring(idx + 1);
}
return name;
}
public static void showUriSnack(Activity activity, Uri uri) {
SnackbarMaker.make(activity, activity.getString(R.string.internal_storage,
"/MagiskManager/" + Utils.getNameFromUri(activity, uri)),
Snackbar.LENGTH_LONG)
.setAction(R.string.ok, (v)->{}).show();
}
} }

View File

@ -6,10 +6,12 @@ import android.webkit.WebResourceRequest;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
public class WebWindow { public class WebWindow {
public WebWindow(String title, String url, Context context) { public WebWindow(String title, String url, Context context) {
AlertDialog.Builder alert = Utils.getAlertDialogBuilder(context); AlertDialog.Builder alert = new AlertDialogBuilder(context);
alert.setTitle(title); alert.setTitle(title);
Logger.dev("WebView: URL = " + url); Logger.dev("WebView: URL = " + url);

View File

@ -48,6 +48,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@ -88,7 +89,7 @@ public class ZipUtils {
buffer.setBuffer(zipAdjust(buffer.toByteArray(), buffer.size())); buffer.setBuffer(zipAdjust(buffer.toByteArray(), buffer.size()));
} }
public static void removeTopFolder(InputStream in, OutputStream out) { public static void removeTopFolder(InputStream in, OutputStream out) throws IOException {
try { try {
JarInputStream source = new JarInputStream(in); JarInputStream source = new JarInputStream(in);
JarOutputStream dest = new JarOutputStream(out); JarOutputStream dest = new JarOutputStream(out);
@ -113,16 +114,20 @@ public class ZipUtils {
dest.close(); dest.close();
in.close(); in.close();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace();
Logger.dev("ZipUtils: removeTopFolder IO error!"); Logger.dev("ZipUtils: removeTopFolder IO error!");
throw e;
} }
} }
public static void unzip(File file, File folder) { public static void unzip(File file, File folder) throws Exception {
unzip(file, folder, ""); unzip(file, folder, "");
} }
public static void unzip(File file, File folder, String path) { public static void unzip(InputStream file, File folder) throws Exception {
unzip(file, folder, "");
}
public static void unzip(File file, File folder, String path) throws Exception {
int count; int count;
FileOutputStream out; FileOutputStream out;
File dest; File dest;
@ -133,32 +138,56 @@ public class ZipUtils {
Enumeration<JarEntry> e = zipfile.entries(); Enumeration<JarEntry> e = zipfile.entries();
while(e.hasMoreElements()) { while(e.hasMoreElements()) {
entry = e.nextElement(); entry = e.nextElement();
if (!entry.getName().contains(path) if (!entry.getName().contains(path) || entry.isDirectory())
|| entry.getName().charAt(entry.getName().length() - 1) == '/') {
// Ignore directories, only create files // Ignore directories, only create files
continue; continue;
}
Logger.dev("ZipUtils: Extracting: " + entry); Logger.dev("ZipUtils: Extracting: " + entry);
is = zipfile.getInputStream(entry); is = zipfile.getInputStream(entry);
dest = new File(folder, entry.getName()); dest = new File(folder, entry.getName());
if (dest.getParentFile().mkdirs()) { if (dest.getParentFile().mkdirs())
dest.createNewFile(); dest.createNewFile();
}
out = new FileOutputStream(dest); out = new FileOutputStream(dest);
while ((count = is.read(data, 0, 4096)) != -1) { while ((count = is.read(data, 0, 4096)) != -1)
out.write(data, 0, count); out.write(data, 0, count);
}
out.flush(); out.flush();
out.close(); out.close();
is.close(); is.close();
} }
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
throw e;
}
}
public static void unzip(InputStream file, File folder, String path) throws Exception {
int count;
FileOutputStream out;
File dest;
JarEntry entry;
byte data[] = new byte[4096];
try (JarInputStream zipfile = new JarInputStream(file)) {
while((entry = zipfile.getNextJarEntry()) != null) {
if (!entry.getName().contains(path) || entry.isDirectory())
// Ignore directories, only create files
continue;
Logger.dev("ZipUtils: Extracting: " + entry);
dest = new File(folder, entry.getName());
if (dest.getParentFile().mkdirs())
dest.createNewFile();
out = new FileOutputStream(dest);
while ((count = zipfile.read(data, 0, 4096)) != -1)
out.write(data, 0, count);
out.flush();
out.close();
}
} catch(Exception e) {
e.printStackTrace();
throw e;
} }
} }
public static void signZip(Context context, InputStream inputStream, public static void signZip(Context context, InputStream inputStream,
OutputStream outputStream, boolean signWholeFile) { OutputStream outputStream, boolean signWholeFile) throws Exception {
JarMap inputJar; JarMap inputJar;
int hashes = 0; int hashes = 0;
try { try {
@ -192,6 +221,7 @@ public class ZipUtils {
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
throw e;
} }
} }
@ -227,6 +257,13 @@ public class ZipUtils {
public Manifest getManifest() { public Manifest getManifest() {
return manifest; return manifest;
} }
public Enumeration<JarEntry> entries() {
Iterator<Map.Entry<String, Pair<JarEntry, ByteArrayOutputStream> >> i = entrySet().iterator();
ArrayList<JarEntry> list = new ArrayList<>();
while (i.hasNext())
list.add(i.next().getValue().first);
return Collections.enumeration(list);
}
} }
/** /**

View File

@ -90,6 +90,7 @@
<string name="permissionNotGranted">This feature will not work without permission to write external storage.</string> <string name="permissionNotGranted">This feature will not work without permission to write external storage.</string>
<string name="no_thanks">No thanks</string> <string name="no_thanks">No thanks</string>
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="ok">OK</string>
<string name="repo_install_title">Install %1$s</string> <string name="repo_install_title">Install %1$s</string>
<string name="repo_install_msg">Do you want to install %1$s ?</string> <string name="repo_install_msg">Do you want to install %1$s ?</string>
<string name="download_install">Download &amp; install</string> <string name="download_install">Download &amp; install</string>
@ -120,6 +121,8 @@
<string name="no_magisksu_title">Not using MagiskSU!</string> <string name="no_magisksu_title">Not using MagiskSU!</string>
<string name="no_magisksu_msg">You are not rooted with MagiskSU, using MagiskHide itself might not be enough!\nIt\'s not officially supported, and you would need additional tools (e.g suhide) to pass Safety Net.</string> <string name="no_magisksu_msg">You are not rooted with MagiskSU, using MagiskHide itself might not be enough!\nIt\'s not officially supported, and you would need additional tools (e.g suhide) to pass Safety Net.</string>
<string name="understand">I understand</string> <string name="understand">I understand</string>
<string name="process_error">Process error</string>
<string name="internal_storage">The zip is stored in:\n[Internal Storage]%1$s</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">General</string> <string name="settings_general_category">General</string>