Add flashing screen

This commit is contained in:
topjohnwu 2017-07-18 00:59:22 +08:00
parent f4097a372b
commit 2ea046cd80
15 changed files with 293 additions and 151 deletions

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.topjohnwu.magisk" xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android" package="com.topjohnwu.magisk">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -22,8 +21,7 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true"/> android:exported="true" />
<activity <activity
android:name=".SplashActivity" android:name=".SplashActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
@ -31,16 +29,22 @@
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".AboutActivity" android:name=".AboutActivity"
android:theme="@style/AppTheme.Transparent"/> android:theme="@style/AppTheme.Transparent" />
<activity <activity
android:name=".SettingsActivity" android:name=".SettingsActivity"
android:theme="@style/AppTheme.Transparent" /> android:theme="@style/AppTheme.Transparent" />
<activity
android:name=".FlashActivity"
android:screenOrientation="nosensor"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppTheme.Transparent" />
<activity <activity
android:name=".superuser.RequestActivity" android:name=".superuser.RequestActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
@ -54,29 +58,26 @@
android:theme="@style/SuRequest" /> android:theme="@style/SuRequest" />
<receiver android:name=".superuser.SuReceiver" /> <receiver android:name=".superuser.SuReceiver" />
<receiver android:name=".receivers.BootReceiver"> <receiver android:name=".receivers.BootReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.PackageReceiver"> <receiver android:name=".receivers.PackageReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" /> <action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" /> <data android:scheme="package" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.ManagerUpdate" /> <receiver android:name=".receivers.ManagerUpdate" />
<service android:name=".services.OnBootIntentService" /> <service android:name=".services.OnBootIntentService" />
<service <service
android:name=".services.UpdateCheckService" android:name=".services.UpdateCheckService"
android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"
android:exported="true" /> android:permission="android.permission.BIND_JOB_SERVICE" />
<provider <provider
android:name="android.support.v4.content.FileProvider" android:name="android.support.v4.content.FileProvider"

View File

@ -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<String> 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<ViewHolder> {
@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);
}
}
}

View File

@ -26,7 +26,7 @@ import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.CheckUpdates; 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.AlertDialogBuilder;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
@ -144,7 +144,13 @@ public class MagiskFragment extends Fragment
@Override @Override
public void onDownloadDone(Uri uri, Context context) { 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, magiskManager.magiskLink,
@ -160,6 +166,28 @@ public class MagiskFragment extends Fragment
.show(); .show();
} }
private static class SetInstallFlags extends ParallelTask<Context, Void, Void> {
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) @OnClick(R.id.uninstall_button)
public void uninstall() { public void uninstall() {
new AlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())

View File

@ -2,7 +2,6 @@ package com.topjohnwu.magisk;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
@ -14,13 +13,11 @@ import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ModulesAdapter; import com.topjohnwu.magisk.adapters.ModulesAdapter;
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;
import com.topjohnwu.magisk.utils.CallbackEvent; import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
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(); Intent intent = new Intent(getActivity(), FlashActivity.class);
new FlashZip(getActivity(), uri).exec(); intent.setData(data.getData()).putExtra("ACTION", "flash");
startActivity(intent);
} }
} }

View File

@ -162,14 +162,14 @@ public class SettingsActivity extends Activity {
new AlertDialogBuilder(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(getActivity()).enable())
.setCancelable(false) .setCancelable(false)
.show(); .show();
} else { } else {
new MagiskHide().enable(); new MagiskHide(getActivity()).enable();
} }
} else { } else {
new MagiskHide().disable(); new MagiskHide(getActivity()).disable();
} }
break; break;
case "hosts": case "hosts":

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.adapters; package com.topjohnwu.magisk.adapters;
import android.app.Activity;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@ -86,10 +87,10 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
holder.checkBox.setChecked(mHideList.contains(info.packageName)); holder.checkBox.setChecked(mHideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) { if (isChecked) {
new MagiskHide().add(info.packageName); new MagiskHide((Activity) holder.itemView.getContext()).add(info.packageName);
mHideList.add(info.packageName); mHideList.add(info.packageName);
} else { } else {
new MagiskHide().rm(info.packageName); new MagiskHide((Activity) holder.itemView.getContext()).rm(info.packageName);
mHideList.remove(info.packageName); mHideList.remove(info.packageName);
} }
}); });

View File

@ -1,14 +1,12 @@
package com.topjohnwu.magisk.asyncs; package com.topjohnwu.magisk.asyncs;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri; import android.net.Uri;
import android.widget.Toast; import android.text.TextUtils;
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.AdaptiveList;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils; import com.topjohnwu.magisk.utils.ZipUtils;
@ -26,11 +24,12 @@ public class FlashZip extends ParallelTask<Void, String, Integer> {
private File mCachedFile, mScriptFile, mCheckFile; private File mCachedFile, mScriptFile, mCheckFile;
private String mFilename; private String mFilename;
private ProgressDialog progress; private AdaptiveList<String> mList;
public FlashZip(Activity context, Uri uri) { public FlashZip(Activity context, Uri uri, AdaptiveList<String> list) {
super(context); super(context);
mUri = uri; mUri = uri;
mList = list;
mCachedFile = new File(magiskManager.getCacheDir(), "install.zip"); mCachedFile = new File(magiskManager.getCacheDir(), "install.zip");
mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary"); mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary");
@ -40,97 +39,77 @@ public class FlashZip extends ParallelTask<Void, String, Integer> {
mFilename = Utils.getNameFromUri(magiskManager, mUri); mFilename = Utils.getNameFromUri(magiskManager, mUri);
} }
private void copyToCache() throws Throwable { private void copyToCache() throws Exception {
publishProgress(magiskManager.getString(R.string.copying_msg)); mList.add(magiskManager.getString(R.string.copying_msg));
if (mCachedFile.exists() && !mCachedFile.delete()) { mCachedFile.delete();
Logger.error("FlashZip: Error while deleting already existing file");
throw new IOException();
}
try ( try (
InputStream in = magiskManager.getContentResolver().openInputStream(mUri); InputStream in = magiskManager.getContentResolver().openInputStream(mUri);
OutputStream outputStream = new FileOutputStream(mCachedFile) OutputStream outputStream = new FileOutputStream(mCachedFile)
) { ) {
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());
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Logger.error("FlashZip: Invalid Uri"); mList.add("! Invalid Uri");
throw e; throw e;
} catch (IOException e) { } catch (IOException e) {
Logger.error("FlashZip: Error in creating file"); mList.add("! Cannot copy to cache");
throw e; throw e;
} }
} }
private boolean unzipAndCheck() throws Exception { private 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 = Utils.readFile(magiskManager.rootShell, mCheckFile.getPath());
ret = Utils.readFile(magiskManager.rootShell, 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) {
magiskManager.rootShell.su_raw(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + MagiskManager.TMP_FOLDER_PATH
);
return ret;
}
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
progress = new ProgressDialog(activity); // UI updates must run in the UI thread
progress.setTitle(R.string.zip_install_progress_title); mList.setCallback(this::publishProgress);
progress.show();
} }
@Override @Override
protected void onProgressUpdate(String... values) { protected void onProgressUpdate(String... values) {
progress.setMessage(values[0]); mList.updateView();
} }
@Override @Override
protected Integer doInBackground(Void... voids) { protected Integer doInBackground(Void... voids) {
Logger.dev("FlashZip Running... " + mFilename);
List<String> ret;
try { try {
copyToCache(); copyToCache();
if (!unzipAndCheck()) return cleanup(0); if (!unzipAndCheck()) return 0;
publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename)); mList.add(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
ret = magiskManager.rootShell.su( magiskManager.rootShell.su(mList,
"BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile, "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile +
"if [ $? -eq 0 ]; then echo true; else echo false; fi" " && echo 'Success!' || echo 'Failed!'"
); );
if (!Utils.isValidShellResponse(ret)) return -1; if (TextUtils.equals(mList.get(mList.size() - 1), "Success!"))
Logger.dev("FlashZip: Console log:"); return 1;
for (String line : ret) { } catch (Exception e) {
Logger.dev(line);
}
if (Boolean.parseBoolean(ret.get(ret.size() - 1)))
return cleanup(1);
} catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
return cleanup(-1); return -1;
} }
// -1 = error, manual install; 0 = invalid zip; 1 = success // -1 = error, manual install; 0 = invalid zip; 1 = success
@Override @Override
protected void onPostExecute(Integer result) { protected void onPostExecute(Integer result) {
progress.dismiss(); magiskManager.rootShell.su_raw(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + MagiskManager.TMP_FOLDER_PATH
);
switch (result) { switch (result) {
case -1: 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); Utils.showUriSnack(activity, mUri);
break; break;
case 0: case 0:
Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show(); mList.add(magiskManager.getString(R.string.invalid_zip));
break; break;
case 1: case 1:
onSuccess(); onSuccess();
@ -140,14 +119,6 @@ public class FlashZip extends ParallelTask<Void, String, Integer> {
} }
protected void onSuccess() { protected void onSuccess() {
magiskManager.updateCheckDone.trigger();
new LoadModules(activity).exec(); 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();
} }
} }

View File

@ -8,8 +8,6 @@ public class MagiskHide extends ParallelTask<Object, Void, Void> {
private boolean isList = false; private boolean isList = false;
public MagiskHide() {}
public MagiskHide(Activity context) { public MagiskHide(Activity context) {
super(context); super(context);
} }

View File

@ -1,56 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
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_process_title),
activity.getString(R.string.zip_unzip_msg));
}
@Override
protected Boolean doInBackground(Void... params) {
if (Shell.rootAccess()) {
magiskManager.rootShell.su_raw("rm -f /dev/.magisk",
(mBoot != null) ? "echo \"BOOTIMAGE=" + mBoot + "\" >> /dev/.magisk" : "",
"echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
"echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk"
);
return true;
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
new FlashZip(activity, mUri).exec();
} else {
Utils.showUriSnack(activity, mUri);
}
super.onPostExecute(result);
}
}

View File

@ -2,9 +2,11 @@ package com.topjohnwu.magisk.asyncs;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
@ -85,13 +87,12 @@ public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
progressDialog.dismiss(); progressDialog.dismiss();
if (result) { if (result) {
if (Shell.rootAccess() && mInstall) { if (Shell.rootAccess() && mInstall) {
new FlashZip(activity, mUri).exec(); magiskManager.startActivity(new Intent(magiskManager, FlashActivity.class).setData(mUri));
} else { } else {
Utils.showUriSnack(activity, mUri); Utils.showUriSnack(activity, mUri);
} }
} else { } else {
Toast.makeText(activity, R.string.process_error, Toast.LENGTH_LONG).show(); magiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
} }
} }
} }

View File

@ -0,0 +1,36 @@
package com.topjohnwu.magisk.utils;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
public class AdaptiveList<E> extends ArrayList<E> {
private Runnable callback;
private RecyclerView mView;
public AdaptiveList(RecyclerView v) {
mView = v;
}
public void updateView() {
mView.getAdapter().notifyDataSetChanged();
mView.scrollToPosition(mView.getAdapter().getItemCount() - 1);
}
public void setCallback(Runnable cb) {
callback = cb;
}
public boolean add(E e) {
boolean ret = super.add(e);
if (ret) {
if (callback == null) {
updateView();
} else {
callback.run();
}
}
return ret;
}
}

View File

@ -44,19 +44,19 @@ public class Utils {
private static final int APK_UPDATE_NOTIFICATION_ID = 2; private static final int APK_UPDATE_NOTIFICATION_ID = 2;
public static boolean itemExist(Shell shell, String path) { public static boolean itemExist(Shell shell, String path) {
String command = "if [ -e " + path + " ]; then echo true; else echo false; fi"; String command = "[ -e " + path + " ] && echo true || echo false";
List<String> ret = shell.su(command); List<String> ret = shell.su(command);
return isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(0)); return isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(0));
} }
public static void createFile(Shell shell, String path) { public static void createFile(Shell shell, String path) {
String folder = path.substring(0, path.lastIndexOf('/')); String folder = path.substring(0, path.lastIndexOf('/'));
String command = "mkdir -p " + folder + " 2>/dev/null; touch " + path + " 2>/dev/null; if [ -f \"" + path + "\" ]; then echo true; else echo false; fi"; String command = "mkdir -p " + folder + " 2>/dev/null; touch " + path + " 2>/dev/null;";
shell.su_raw(command); shell.su_raw(command);
} }
public static void removeItem(Shell shell, String path) { public static void removeItem(Shell shell, String path) {
String command = "rm -rf " + path + " 2>/dev/null; if [ -e " + path + " ]; then echo false; else echo true; fi"; String command = "rm -rf " + path + " 2>/dev/null";
shell.su_raw(command); shell.su_raw(command);
} }

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@android:color/black"
tools:context="com.topjohnwu.magisk.FlashActivity">
<include layout="@layout/toolbar"/>
<HorizontalScrollView
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp">
<android.support.v7.widget.RecyclerView
android:id="@+id/flash_logs"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager">
</android.support.v7.widget.RecyclerView>
</HorizontalScrollView>
<LinearLayout
android:id="@+id/button_panel"
style="?android:buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@android:color/darker_gray"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/no_thanks"
style="?android:borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:text="@string/close" />
<Button
android:id="@+id/reboot"
style="?android:borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:text="@string/reboot" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:textColor="@android:color/white"
android:textSize="10sp" />

View File

@ -133,6 +133,7 @@
<string name="manager_update_title">New Magisk Manager Update Available!</string> <string name="manager_update_title">New Magisk Manager Update Available!</string>
<string name="manager_download_install">Press to download and install</string> <string name="manager_download_install">Press to download and install</string>
<string name="magisk_updates">Magisk Updates</string> <string name="magisk_updates">Magisk Updates</string>
<string name="flashing">Flashing</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">General</string> <string name="settings_general_category">General</string>