From bbae93aa16c63743da74fdb779f2bb3364523156 Mon Sep 17 00:00:00 2001 From: d8ahazard Date: Sat, 22 Oct 2016 11:05:34 -0500 Subject: [PATCH] Initial re-add of hide fragment --- .../com/topjohnwu/magisk/AboutActivity.java | 2 +- .../topjohnwu/magisk/AutoRootFragment.java | 172 ++++++++++++++++++ .../topjohnwu/magisk/SettingsFragment.java | 153 ++++++++++++++++ .../com/topjohnwu/magisk/SplashActivity.java | 34 +++- .../magisk/utils/ApplicationAdapter.java | 114 ++++++++++++ .../com/topjohnwu/magisk/utils/Utils.java | 11 ++ app/src/main/res/drawable/ic_autoroot.xml | 4 + .../main/res/drawable/ic_autoroot_white.xml | 4 + .../main/res/layout/auto_root_fragment.xml | 14 ++ app/src/main/res/layout/list_item_app.xml | 89 +++++++++ app/src/main/res/menu/drawer.xml | 6 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 5 +- app/src/main/res/xml/accessibilityservice.xml | 11 ++ app/src/main/res/xml/app_settings.xml | 28 +++ 15 files changed, 645 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java create mode 100644 app/src/main/java/com/topjohnwu/magisk/SettingsFragment.java create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java create mode 100644 app/src/main/res/drawable/ic_autoroot.xml create mode 100644 app/src/main/res/drawable/ic_autoroot_white.xml create mode 100644 app/src/main/res/layout/auto_root_fragment.xml create mode 100644 app/src/main/res/layout/list_item_app.xml create mode 100644 app/src/main/res/xml/accessibilityservice.xml diff --git a/app/src/main/java/com/topjohnwu/magisk/AboutActivity.java b/app/src/main/java/com/topjohnwu/magisk/AboutActivity.java index 047854c7b..d099a742f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/AboutActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/AboutActivity.java @@ -25,7 +25,7 @@ import java.io.InputStream; import butterknife.BindView; import butterknife.ButterKnife; -public class AboutActivity extends AppCompatActivity { +public class AboutActivity extends AppCompatActivity { private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager"; private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382"; diff --git a/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java b/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java new file mode 100644 index 000000000..ff6691c30 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java @@ -0,0 +1,172 @@ +package com.topjohnwu.magisk; + +import android.app.ListFragment; +import android.app.ProgressDialog; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.Nullable; + +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import com.topjohnwu.magisk.utils.ApplicationAdapter; +import com.topjohnwu.magisk.utils.Logger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class AutoRootFragment extends ListFragment { + private PackageManager packageManager = null; + private List applist = null; + private ApplicationAdapter listadaptor = null; + public ListView listView; + public SharedPreferences prefs; + List arrayBlackList; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + View view = inflater.inflate(R.layout.auto_root_fragment, container, false); + int horizontalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); + int verticalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); + TypedValue tv = new TypedValue(); + int actionBarHeight = 130; + if (getActivity().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { + actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics()); + } + + view.setPadding(horizontalMargin, actionBarHeight, horizontalMargin, verticalMargin); + + return view; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + + } + + @Override + public void onResume() { + super.onResume(); + initializeElements(); + super.onResume(); + getActivity().setTitle(R.string.auto_toggle); + + + } + + private void initializeElements() { + listView = getListView(); + packageManager = getActivity().getPackageManager(); + new LoadApplications().execute(); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + Logger.dev("Click"); + super.onListItemClick(l, v, position, id); + ApplicationInfo app = applist.get(position); + ToggleApp(app.packageName, position, v); + + } + + private void ToggleApp(String appToCheck, int position, View v) { + Logger.dev("Magisk", "AutoRootFragment: ToggleApp called for " + appToCheck); + Set blackListSet = prefs.getStringSet("auto_blacklist", null); + assert blackListSet != null; + arrayBlackList = new ArrayList<>(blackListSet); + + if (!arrayBlackList.contains(appToCheck)) { + arrayBlackList.add(appToCheck); + + } else { + for (int i = 0; i < arrayBlackList.size(); i++) { + if (appToCheck.equals(arrayBlackList.get(i))) { + arrayBlackList.remove(i); + } + } + + } + Logger.dev("Committing set, value is: " + arrayBlackList.toString()); + SharedPreferences.Editor editor = prefs.edit(); + editor.putStringSet("auto_blacklist", new HashSet<>(arrayBlackList)); + editor.apply(); + listadaptor.UpdateRootStatusView(position, v); + + } + + private List checkForLaunchIntent(List list) { + ArrayList applist = new ArrayList<>(); + for (ApplicationInfo info : list) { + try { + if (null != packageManager.getLaunchIntentForPackage(info.packageName)) { + if (!info.packageName.contains("magisk")) { + applist.add(info); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + Collections.sort(applist, new CustomComparator()); + + return applist; + } + + public class CustomComparator implements Comparator { + @Override + public int compare(ApplicationInfo o1, ApplicationInfo o2) { + return o1.loadLabel(packageManager).toString().compareToIgnoreCase(o2.loadLabel(packageManager).toString()); + } + } + + private class LoadApplications extends AsyncTask { + private ProgressDialog progress = null; + + @Override + protected Void doInBackground(Void... params) { + applist = checkForLaunchIntent(packageManager.getInstalledApplications(PackageManager.GET_META_DATA)); + listadaptor = new ApplicationAdapter(getActivity(), + R.layout.list_item_app, applist); + + return null; + } + + @Override + protected void onCancelled() { + super.onCancelled(); + } + + @Override + protected void onPostExecute(Void result) { + setListAdapter(listadaptor); + progress.dismiss(); + super.onPostExecute(result); + } + + @Override + protected void onPreExecute() { + progress = ProgressDialog.show(getActivity(), null, + "Loading application info..."); + super.onPreExecute(); + } + + @Override + protected void onProgressUpdate(Void... values) { + super.onProgressUpdate(values); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsFragment.java b/app/src/main/java/com/topjohnwu/magisk/SettingsFragment.java new file mode 100644 index 000000000..2b2d33972 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/SettingsFragment.java @@ -0,0 +1,153 @@ +package com.topjohnwu.magisk; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.topjohnwu.magisk.utils.Async; +import com.topjohnwu.magisk.utils.Logger; +import com.topjohnwu.magisk.utils.Utils; + +import butterknife.ButterKnife; + +public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { + private ListPreference themePreference; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.uisettings); + PreferenceManager.setDefaultValues(getActivity(), R.xml.uisettings, false); + } + + @Override + public void onResume() { + super.onResume(); + getActivity().setTitle(R.string.settings); + PreferenceManager.getDefaultSharedPreferences(getActivity()).registerOnSharedPreferenceChangeListener(this); + + } + + @Override + public void onDestroy() { + super.onDestroy(); + PreferenceManager.getDefaultSharedPreferences(getActivity()).unregisterOnSharedPreferenceChangeListener(this); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + themePreference = (ListPreference) findPreference("theme"); + CheckBoxPreference busyboxPreference = (CheckBoxPreference) findPreference("busybox"); + CheckBoxPreference quickTilePreference = (CheckBoxPreference) findPreference("enable_quicktile"); + busyboxPreference.setChecked(Utils.commandExists("unzip")); + PreferenceManager.getDefaultSharedPreferences(getActivity()).registerOnSharedPreferenceChangeListener(this); + CheckBoxPreference keepRootOffPreference = (CheckBoxPreference) findPreference("keep_root_off"); + CheckBoxPreference hideRootNotificationPreference = (CheckBoxPreference) findPreference("hide_root_notification"); + themePreference.setSummary(themePreference.getValue()); + if (MagiskFragment.magiskVersion == -1) { + quickTilePreference.setEnabled(false); + keepRootOffPreference.setEnabled(false); + hideRootNotificationPreference.setEnabled(false); + busyboxPreference.setEnabled(false); + } else { + quickTilePreference.setEnabled(true); + keepRootOffPreference.setEnabled(true); + hideRootNotificationPreference.setEnabled(true); + busyboxPreference.setEnabled(true); + } + + // calculate margins + int horizontalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); + int verticalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); + TypedValue tv = new TypedValue(); + int actionBarHeight = 130; + if (getActivity().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { + actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics()); + } + + view.setPadding(horizontalMargin, actionBarHeight, horizontalMargin, verticalMargin); + + return view; + + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Logger.dev("Settings: NewValue is " + key); + + if (key.equals("theme")) { + String pref = sharedPreferences.getString(key, ""); + + themePreference.setSummary(pref); + if (pref.equals("Dark")) { + getActivity().getApplication().setTheme(R.style.AppTheme_dh); + } else { + getActivity().getApplication().setTheme(R.style.AppTheme); + } + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("Relaunch", "Settings"); + startActivity(intent); + + Logger.dev("SettingsFragment: theme is " + pref); + + } else if (key.equals("enable_quicktile")) { + boolean checked = sharedPreferences.getBoolean("enable_quicktile", false); + if (checked) { + new AsyncTask () { + @Override + protected Boolean doInBackground(Void... voids) { + return Utils.installTile(getActivity()); + } + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (result) { + Toast.makeText(getActivity(), "Tile installed", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getActivity(), "Tile installation error", Toast.LENGTH_SHORT).show(); + } + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } else { + new AsyncTask () { + @Override + protected Boolean doInBackground(Void... voids) { + return Utils.uninstallTile(getActivity()); + } + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (result) { + Toast.makeText(getActivity(), "Tile uninstalled", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getActivity(), "Tile uninstallation error", Toast.LENGTH_SHORT).show(); + } + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } + } else if (key.equals("busybox")) { + boolean checked = sharedPreferences.getBoolean("busybox", false); + new Async.LinkBusyBox(checked).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } else if (key.equals("developer_logging")) { + Logger.devLog = sharedPreferences.getBoolean("developer_logging", false); + } else if (key.equals("shell_logging")) { + Logger.logShell = sharedPreferences.getBoolean("shell_logging", false); + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java b/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java index d85529d5a..607a2c7e9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java @@ -7,6 +7,7 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; +import com.topjohnwu.magisk.services.MonitorService; import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Utils; @@ -25,12 +26,43 @@ public class SplashActivity extends AppCompatActivity { Logger.devLog = prefs.getBoolean("developer_logging", false); Logger.logShell = prefs.getBoolean("shell_logging", false); + // Set up default preferences,make sure we add "extra" blacklist entries. + if (!defaultPrefs.contains("auto_blacklist")) { + Logger.dev("SplashActivity: Setting default preferences for application"); + SharedPreferences.Editor editor = defaultPrefs.edit(); + Set set = new HashSet<>(); + set.add("com.google.android.apps.walletnfcrel"); + set.add("com.google.android.gms"); + set.add("com.google.commerce.tapandpay"); + editor.putStringSet("auto_blacklist", set); + editor.putBoolean("autoRootEnable", false); + editor.putBoolean("root", Utils.rootEnabled()); + editor.apply(); + } + + // Set up toggle states based on preferences, start services, disable root if set + if (Utils.autoToggleEnabled(getApplicationContext())) { + if (!Utils.hasServicePermission(getApplicationContext())) { + Utils.toggleAutoRoot(false, getApplicationContext()); + } + } + if (Utils.autoToggleEnabled(getApplicationContext())) { + if (!Utils.isMyServiceRunning(MonitorService.class, getApplicationContext())) { + Intent myIntent = new Intent(getApplication(), MonitorService.class); + getApplication().startService(myIntent); + } + } else if (defaultPrefs.getBoolean("keep_root_off", false)) { + Utils.toggleRoot(false, getApplication()); + } + + // Set up quick settings tile + Utils.setupQuickSettingsTile(getApplicationContext()); + // Initialize prefs.edit() .putBoolean("module_done", false) .putBoolean("repo_done", false) .putBoolean("update_check_done", false) - .putBoolean("root", Utils.rootEnabled()) .apply(); new Async.CheckUpdates(prefs).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java b/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java new file mode 100644 index 000000000..f55b68d99 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java @@ -0,0 +1,114 @@ +package com.topjohnwu.magisk.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; + +import com.topjohnwu.magisk.R; + +import java.util.List; +import java.util.Set; + +public class ApplicationAdapter extends ArrayAdapter { + private List appsList = null; + private Context context; + private PackageManager packageManager; + public SharedPreferences prefs; + + public ApplicationAdapter(Context context, int textViewResourceId, + List appsList) { + super(context, textViewResourceId, appsList); + this.context = context; + this.appsList = appsList; + packageManager = context.getPackageManager(); + } + + @Override + public int getCount() { + return ((null != appsList) ? appsList.size() : 0); + } + + @Override + public ApplicationInfo getItem(int position) { + return ((null != appsList) ? appsList.get(position) : null); + } + + @Override + public long getItemId(int position) { + return position; + } + + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + + prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + if (null == view) { + LayoutInflater layoutInflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = layoutInflater.inflate(R.layout.list_item_app, null); + } + + ApplicationInfo applicationInfo = appsList.get(position); + if (null != applicationInfo) { + TextView appName = (TextView) view.findViewById(R.id.app_name); + TextView packageName = (TextView) view.findViewById(R.id.app_paackage); + ImageView iconview = (ImageView) view.findViewById(R.id.app_icon); + CheckBox statusview = (CheckBox) view.findViewById(R.id.checkbox); + appName.setText(applicationInfo.loadLabel(packageManager)); + packageName.setText(applicationInfo.packageName); + iconview.setImageDrawable(applicationInfo.loadIcon(packageManager)); + if (CheckApp(applicationInfo.packageName)) { + statusview.setChecked(true); + } else { + statusview.setChecked(false); + } + } + return view; + } + + public void UpdateRootStatusView(int position, View convertView) { + View view = convertView; + if (null == view) { + LayoutInflater layoutInflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = layoutInflater.inflate(R.layout.list_item_app, null); + } + ApplicationInfo applicationInfo = appsList.get(position); + if (null != applicationInfo) { + CheckBox statusview = (CheckBox) view.findViewById(R.id.checkbox); + if (CheckApp(applicationInfo.packageName)) { + statusview.setChecked(true); + } else { + statusview.setChecked(false); + } + } + + } + + private boolean CheckApp(String appToCheck) { + boolean starter = false; + Set set = prefs.getStringSet("auto_blacklist", null); + if (set != null) { + for (String string : set) { + if (string.equals(appToCheck)) { + starter = true; + } + } + } + + return starter; + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index 945f008fb..5e3b51b36 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -13,6 +13,10 @@ import android.widget.Toast; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.receivers.DownloadReceiver; +import com.topjohnwu.magisk.receivers.PrivateBroadcastReceiver; +import com.topjohnwu.magisk.services.MonitorService; +import com.topjohnwu.magisk.services.TileServiceCompat; +import com.topjohnwu.magisk.services.TileServiceNewApi; import java.io.File; import java.io.UnsupportedEncodingException; @@ -60,6 +64,13 @@ public class Utils { return commandExists("su"); } + public static boolean autoToggleEnabled(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + Logger.dev("Utils: AutoRootEnableCheck is " + preferences.getBoolean("autoRootEnable", false)); + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("autoRootEnable", false); + + } + public static boolean createFile(String path) { String command = "touch " + path + " 2>/dev/null; if [ -f " + path + " ]; then echo true; else echo false; fi"; return Shell.rootAccess() && Boolean.parseBoolean(Shell.su(command).get(0)); diff --git a/app/src/main/res/drawable/ic_autoroot.xml b/app/src/main/res/drawable/ic_autoroot.xml new file mode 100644 index 000000000..a8f96a803 --- /dev/null +++ b/app/src/main/res/drawable/ic_autoroot.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_autoroot_white.xml b/app/src/main/res/drawable/ic_autoroot_white.xml new file mode 100644 index 000000000..ec532328f --- /dev/null +++ b/app/src/main/res/drawable/ic_autoroot_white.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/layout/auto_root_fragment.xml b/app/src/main/res/layout/auto_root_fragment.xml new file mode 100644 index 000000000..a3a15447b --- /dev/null +++ b/app/src/main/res/layout/auto_root_fragment.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_app.xml b/app/src/main/res/layout/list_item_app.xml new file mode 100644 index 000000000..b3402a2fb --- /dev/null +++ b/app/src/main/res/layout/list_item_app.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml index d3817b115..71dec8ffa 100644 --- a/app/src/main/res/menu/drawer.xml +++ b/app/src/main/res/menu/drawer.xml @@ -10,6 +10,8 @@ android:icon="@drawable/magisk" android:title="@string/magisk"/> + + android:id="@+id/log" android:icon="@drawable/ic_bug_report" android:title="@string/log"/> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a3d1901d2..2f67c4f6b 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -11,4 +11,5 @@ 300dip 3dp 10dp + 50dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68474d228..7a76894fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ Open navigation drawer Close navigation drawer + MagiskHide Modules Downloads Log @@ -42,6 +43,7 @@ Save to SD Send Reload + SD card not found or not writable Clear log now Log successfully cleared Could not clear the log: @@ -53,7 +55,7 @@ Main developers topjohnwu in collaboration with Digitalhigh and Dvdandroid.]]> App\'s changelog - + App\'s version Source code Donation @@ -64,6 +66,7 @@ This feature will not work without permission to write external storage. No root access, functionality limited No thanks + Accessibility service required for Magisk auto-unroot feature %1$s Update! New version v%2$s of %1$s is available!\nChangelog:\n%3$s Install %1$s diff --git a/app/src/main/res/xml/accessibilityservice.xml b/app/src/main/res/xml/accessibilityservice.xml new file mode 100644 index 000000000..08bf96e91 --- /dev/null +++ b/app/src/main/res/xml/accessibilityservice.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/xml/app_settings.xml b/app/src/main/res/xml/app_settings.xml index b56b59563..5ed9f4448 100644 --- a/app/src/main/res/xml/app_settings.xml +++ b/app/src/main/res/xml/app_settings.xml @@ -31,6 +31,34 @@ + + + + + + + + + + + + + +