diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/ApplicationAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/ApplicationAdapter.java deleted file mode 100644 index 6c7d86eeb..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/ApplicationAdapter.java +++ /dev/null @@ -1,426 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.ComponentInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.AsyncTask; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.TextView; - -import com.buildware.widget.indeterm.IndeterminateCheckBox; -import com.topjohnwu.magisk.App; -import com.topjohnwu.magisk.Config; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.utils.Event; -import com.topjohnwu.magisk.utils.Utils; -import com.topjohnwu.magisk.view.ArrowExpandable; -import com.topjohnwu.magisk.view.Expandable; -import com.topjohnwu.superuser.Shell; -import com.topjohnwu.superuser.internal.UiThreadHandler; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; -import androidx.collection.ArraySet; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import java9.util.Comparators; -import java9.util.Lists; -import java9.util.Objects; -import java9.util.Sets; -import java9.util.stream.Collectors; -import java9.util.stream.Stream; -import java9.util.stream.StreamSupport; - -public class ApplicationAdapter extends SectionedAdapter - { - - private static final String SAFETYNET_PROCESS = "com.google.android.gms.unstable"; - private static final String GMS_PACKAGE = "com.google.android.gms"; - private static boolean old_hide = false; - - /* A list of apps that should not be shown as hide-able */ - private static final List HIDE_BLACKLIST = Lists.of( - App.self.getPackageName(), - "android", - "com.android.chrome", - "com.chrome.beta", - "com.chrome.dev", - "com.chrome.canary", - "com.android.webview", - "com.google.android.webview" - ); - private static final List DEFAULT_HIDELIST = Lists.of( - SAFETYNET_PROCESS - ); - - private static int BOTTOM_MARGIN = -1; - - private List fullList, showList; - private List hideList; - private PackageManager pm; - private boolean showSystem; - - public ApplicationAdapter(Context context) { - fullList = showList = Collections.emptyList(); - hideList = Collections.emptyList(); - pm = context.getPackageManager(); - showSystem = Config.get(Config.Key.SHOW_SYSTEM_APP); - AsyncTask.SERIAL_EXECUTOR.execute(this::loadApps); - } - - private static ViewGroup.MarginLayoutParams getMargins(RecyclerView.ViewHolder vh) { - return (ViewGroup.MarginLayoutParams) vh.itemView.getLayoutParams(); - } - - @Override - public int getSectionCount() { - return showList.size(); - } - - @Override - public int getItemCount(int section) { - HideAppInfo app = showList.get(section); - return app.expanded ? app.processList.size() : 0; - } - - @Override - public AppViewHolder onCreateSectionViewHolder(ViewGroup parent) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_hide_app, parent, false); - AppViewHolder vh = new AppViewHolder(v); - if (BOTTOM_MARGIN < 0) - BOTTOM_MARGIN = getMargins(vh).bottomMargin; - return vh; - } - - @Override - public ProcessViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_hide_process, parent, false); - return new ProcessViewHolder(v); - } - - @Override - public void onBindSectionViewHolder(AppViewHolder holder, int section) { - HideAppInfo app = showList.get(section); - holder.app_name.setText(app.name); - holder.app_icon.setImageDrawable(app.info.loadIcon(pm)); - holder.package_name.setText(app.info.packageName); - holder.checkBox.setOnStateChangedListener(null); - holder.checkBox.setState(app.getState()); - holder.ex.setExpanded(app.expanded); - - int index = getItemPosition(section, 0); - holder.checkBox.setOnStateChangedListener((IndeterminateCheckBox box, @Nullable Boolean status) -> { - if (status != null) { - setHide(status, app); - if (app.expanded) - notifyItemRangeChanged(index, app.processList.size()); - } - }); - - if (app.processList.size() > 1) { - holder.arrow.setVisibility(View.VISIBLE); - - holder.trigger.setOnClickListener((v) -> { - if (app.expanded) { - app.expanded = false; - notifyItemRangeRemoved(index, app.processList.size()); - holder.ex.collapse(); - } else { - app.expanded = true; - notifyItemRangeInserted(index, app.processList.size()); - holder.ex.expand(); - } - }); - } else { - holder.arrow.setVisibility(View.GONE); - holder.trigger.setOnClickListener(null); - } - - } - - @Override - public void onBindItemViewHolder(ProcessViewHolder holder, int section, int position) { - HideAppInfo app = showList.get(section); - HideProcessInfo target = app.processList.get(position); - holder.process.setText(target.name); - holder.checkbox.setOnCheckedChangeListener(null); - holder.checkbox.setChecked(target.hidden); - holder.checkbox.setOnCheckedChangeListener((v, checked) -> { - setHide(checked, app, target); - notifyItemChanged(getSectionPosition(section)); - }); - getMargins(holder).bottomMargin = - position == app.processList.size() - 1 ? BOTTOM_MARGIN : 0; - } - - public void filter(String constraint) { - AsyncTask.SERIAL_EXECUTOR.execute(() -> { - Stream s = StreamSupport.stream(fullList) - .filter(this::systemFilter) - .filter(t -> nameFilter(t, constraint)); - UiThreadHandler.run(() -> { - showList = s.collect(Collectors.toList()); - notifyDataSetChanged(); - }); - }); - } - - public void setShowSystem(boolean b) { - showSystem = b; - } - - public void refresh() { - AsyncTask.SERIAL_EXECUTOR.execute(this::loadApps); - } - - private void setHide(boolean add, HideAppInfo app) { - if (add) { - StreamSupport.stream(app.processList).forEach(p -> setHide(true, app, p)); - } else { - if (StreamSupport.stream(app.processList) - .anyMatch(p -> p.name.equals(SAFETYNET_PROCESS))) { - StreamSupport.stream(app.processList).forEach(p -> setHide(false, app, p)); - } else { - // Quick removal - Shell.su("magiskhide --rm " + app.info.packageName).submit(); - StreamSupport.stream(app.processList).forEach(p -> p.hidden = false); - } - } - } - - private void setHide(boolean add, HideAppInfo app, HideProcessInfo process) { - // Don't remove SafetyNet - if (!add && DEFAULT_HIDELIST.contains(process.name)) - return; - Shell.su(Utils.fmt("magiskhide --%s %s %s", add ? "add" : "rm", - app.info.packageName, process.name)).submit(); - process.hidden = add; - } - - private void addProcesses(Set set, ComponentInfo[] infos) { - if (infos != null) - for (ComponentInfo info : infos) - set.add(info.processName); - } - - private PackageInfo getPackageInfo(String pkg) { - // Try super hard to get as much info as possible - try { - return pm.getPackageInfo(pkg, - PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | - PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS); - } catch (Exception e1) { - try { - PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_ACTIVITIES); - info.services = pm.getPackageInfo(pkg, PackageManager.GET_SERVICES).services; - info.receivers = pm.getPackageInfo(pkg, PackageManager.GET_RECEIVERS).receivers; - info.providers = pm.getPackageInfo(pkg, PackageManager.GET_PROVIDERS).providers; - return info; - } catch (Exception e2) { - return null; - } - } - } - - @WorkerThread - private void loadApps() { - hideList = StreamSupport.stream(Shell.su("magiskhide --ls").exec().getOut()) - .map(HideTarget::new) - .collect(Collectors.toList()); - - fullList = StreamSupport.stream(pm.getInstalledApplications(0)) - .filter(info -> !HIDE_BLACKLIST.contains(info.packageName) && info.enabled) - .map(info -> { - if (old_hide) { - return new HideAppInfo(info, Sets.of(info.packageName)); - } else { - Set set = new ArraySet<>(); - PackageInfo pkg = getPackageInfo(info.packageName); - if (pkg != null) { - addProcesses(set, pkg.activities); - addProcesses(set, pkg.services); - addProcesses(set, pkg.receivers); - addProcesses(set, pkg.providers); - } - if (set.isEmpty()) - return null; - return new HideAppInfo(info, set); - } - }).filter(Objects::nonNull).sorted() - .collect(Collectors.toList()); - - Event.trigger(false, Event.MAGISK_HIDE_DONE); - } - - // True if not system app or user already hidden it - private boolean systemFilter(HideAppInfo target) { - return showSystem || target.haveHidden() || - (target.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0; - } - - private boolean contains(String s, String filter) { - return s.toLowerCase().contains(filter); - } - - private boolean nameFilter(HideAppInfo target, String filter) { - if (filter == null || filter.isEmpty()) - return true; - filter = filter.toLowerCase(); - if (contains(target.name, filter)) - return true; - for (HideProcessInfo p : target.processList) { - if (contains(p.name, filter)) - return true; - } - return contains(target.info.packageName, filter); - } - - class HideAppInfo implements Comparable { - String name; - ApplicationInfo info; - List processList; - boolean expanded; - IndeterminateCheckBox.OnStateChangedListener listener; - - HideAppInfo(ApplicationInfo appInfo, Set set) { - info = appInfo; - name = Utils.getAppLabel(info, pm); - expanded = false; - processList = StreamSupport.stream(set) - .map(process -> new HideProcessInfo(info.packageName, process)) - .sorted().collect(Collectors.toList()); - listener = (IndeterminateCheckBox box, @Nullable Boolean status) -> { - if (status != null) { - for (HideProcessInfo p : processList) { - String cmd = Utils.fmt("magiskhide --%s %s %s", - status ? "add" : "rm", info.packageName, p.name); - Shell.su(cmd).submit(); - p.hidden = status; - } - } - }; - } - - @Override - public int compareTo(HideAppInfo o) { - Comparator c; - c = Comparators.comparing(HideAppInfo::haveHidden); - c = Comparators.reversed(c); - c = Comparators.thenComparing(c, t -> t.name, String::compareToIgnoreCase); - c = Comparators.thenComparing(c, t -> t.info.packageName); - return c.compare(this, o); - } - - Boolean getState() { - boolean all = true; - boolean hidden = false; - for (HideProcessInfo p : processList) { - if (!p.hidden) - all = false; - else - hidden = true; - } - if (all) - return true; - return hidden ? null : false; - } - - boolean haveHidden() { - Boolean c = getState(); - return c == null ? true : c; - } - } - - class HideProcessInfo implements Comparable { - String name; - boolean hidden; - - HideProcessInfo(String pkg, String process) { - this.name = process; - for (HideTarget t : hideList) { - if (t.pkg.equals(pkg) && t.process.equals(process)) { - hidden = true; - break; - } - } - } - - @Override - public int compareTo(HideProcessInfo o) { - Comparator c; - c = Comparators.comparing((HideProcessInfo t) -> t.hidden); - c = Comparators.reversed(c); - c = Comparators.thenComparing(c, t -> t.name); - return c.compare(this, o); - } - } - - class HideTarget { - String pkg; - String process; - - HideTarget(String line) { - String[] split = line.split("\\|", 2); - pkg = split[0]; - if (split.length == 2) { - process = split[1]; - } else { - // Backwards compatibility - old_hide = true; - process = pkg; - } - } - } - - class AppViewHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.app_icon) ImageView app_icon; - @BindView(R.id.app_name) TextView app_name; - @BindView(R.id.package_name) TextView package_name; - @BindView(R.id.checkbox) IndeterminateCheckBox checkBox; - @BindView(R.id.trigger) View trigger; - @BindView(R.id.arrow) ImageView arrow; - - Expandable ex; - - AppViewHolder(@NonNull View itemView) { - super(itemView); - new ApplicationAdapter$AppViewHolder_ViewBinding(this, itemView); - ex = new ArrowExpandable(new Expandable() { - @Override - protected void onExpand() { - getMargins(AppViewHolder.this).bottomMargin = 0; - } - - @Override - protected void onCollapse() { - getMargins(AppViewHolder.this).bottomMargin = BOTTOM_MARGIN; - } - }, arrow); - } - } - - class ProcessViewHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.process) TextView process; - @BindView(R.id.checkbox) CheckBox checkbox; - - ProcessViewHolder(@NonNull View itemView) { - super(itemView); - new ApplicationAdapter$ProcessViewHolder_ViewBinding(this, itemView); - } - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/ModulesAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/ModulesAdapter.java deleted file mode 100644 index e82481ffa..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/ModulesAdapter.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - -import android.content.Context; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.TextView; - -import com.google.android.material.snackbar.Snackbar; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.model.entity.Module; -import com.topjohnwu.magisk.view.SnackbarMaker; -import com.topjohnwu.superuser.Shell; - -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; - -public class ModulesAdapter extends RecyclerView.Adapter { - - private final List mList; - - public ModulesAdapter(List list) { - mList = list; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false); - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(final ViewHolder holder, int position) { - Context context = holder.itemView.getContext(); - final Module module = mList.get(position); - - String version = module.getVersion(); - String author = module.getAuthor(); - String description = module.getDescription(); - String noInfo = context.getString(R.string.no_info_provided); - - holder.title.setText(module.getName()); - holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version); - holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author)); - holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description); - - holder.checkBox.setOnCheckedChangeListener(null); - holder.checkBox.setChecked(module.isEnabled()); - holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { - int snack; - if (isChecked) { - module.removeDisableFile(); - snack = R.string.disable_file_removed; - } else { - module.createDisableFile(); - snack = R.string.disable_file_created; - } - SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); - }); - - holder.delete.setOnClickListener(v -> { - boolean removed = module.willBeRemoved(); - int snack; - if (removed) { - module.deleteRemoveFile(); - snack = R.string.remove_file_deleted; - } else { - module.createRemoveFile(); - snack = R.string.remove_file_created; - } - SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); - updateDeleteButton(holder, module); - }); - - if (module.isUpdated()) { - holder.notice.setVisibility(View.VISIBLE); - holder.notice.setText(R.string.update_file_created); - holder.delete.setEnabled(false); - } else { - updateDeleteButton(holder, module); - } - } - - private void updateDeleteButton(ViewHolder holder, Module module) { - holder.notice.setVisibility(module.willBeRemoved() ? View.VISIBLE : View.GONE); - - if (module.willBeRemoved()) { - holder.delete.setImageResource(R.drawable.ic_undelete); - } else { - holder.delete.setImageResource(R.drawable.ic_delete); - } - } - - @Override - public int getItemCount() { - return mList.size(); - } - - static class ViewHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.title) TextView title; - @BindView(R.id.version_name) TextView versionName; - @BindView(R.id.description) TextView description; - @BindView(R.id.notice) TextView notice; - @BindView(R.id.checkbox) CheckBox checkBox; - @BindView(R.id.author) TextView author; - @BindView(R.id.delete) ImageView delete; - - ViewHolder(View itemView) { - super(itemView); - new ModulesAdapter$ViewHolder_ViewBinding(this, itemView); - - if (!Shell.rootAccess()) { - checkBox.setEnabled(false); - delete.setEnabled(false); - } - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/PolicyAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/PolicyAdapter.java deleted file mode 100644 index ef4e0bfec..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/PolicyAdapter.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.pm.PackageManager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.google.android.material.snackbar.Snackbar; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.data.database.MagiskDB; -import com.topjohnwu.magisk.model.entity.Policy; -import com.topjohnwu.magisk.utils.FingerprintHelper; -import com.topjohnwu.magisk.view.ArrowExpandable; -import com.topjohnwu.magisk.view.Expandable; -import com.topjohnwu.magisk.view.ExpandableViewHolder; -import com.topjohnwu.magisk.view.SnackbarMaker; -import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog; -import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog; - -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.SwitchCompat; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; - -public class PolicyAdapter extends RecyclerView.Adapter { - - private final List policyList; - private final MagiskDB dbHelper; - private final PackageManager pm; - private final boolean[] expandList; - - public PolicyAdapter(List list, MagiskDB db, PackageManager pm) { - policyList = list; - expandList = new boolean[policyList.size()]; - dbHelper = db; - this.pm = pm; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false); - return new ViewHolder(v); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - Policy policy = policyList.get(position); - - holder.settings.setExpanded(expandList[position]); - holder.trigger.setOnClickListener(view -> { - if (holder.settings.isExpanded()) { - holder.settings.collapse(); - expandList[position] = false; - } else { - holder.settings.expand(); - expandList[position] = true; - } - }); - - holder.appName.setText(policy.appName); - holder.packageName.setText(policy.packageName); - holder.appIcon.setImageDrawable(policy.info.loadIcon(pm)); - - holder.notificationSwitch.setOnCheckedChangeListener(null); - holder.loggingSwitch.setOnCheckedChangeListener(null); - - holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW); - holder.notificationSwitch.setChecked(policy.notification); - holder.loggingSwitch.setChecked(policy.logging); - - holder.masterSwitch.setOnClickListener(v -> { - boolean isChecked = holder.masterSwitch.isChecked(); - Runnable r = () -> { - if (isChecked ? policy.policy == Policy.DENY : policy.policy == Policy.ALLOW) { - policy.policy = isChecked ? Policy.ALLOW : Policy.DENY; - String message = v.getContext().getString( - isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName); - SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); - dbHelper.updatePolicy(policy); - } - }; - if (FingerprintHelper.useFingerprint()) { - holder.masterSwitch.setChecked(!isChecked); - new FingerprintAuthDialog((Activity) v.getContext(), () -> { - holder.masterSwitch.setChecked(isChecked); - r.run(); - }).show(); - } else { - r.run(); - } - }); - holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> { - if (isChecked != policy.notification) { - policy.notification = isChecked; - String message = v.getContext().getString( - isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName); - SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); - dbHelper.updatePolicy(policy); - } - }); - holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> { - if (isChecked != policy.logging) { - policy.logging = isChecked; - String message = v.getContext().getString( - isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName); - SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); - dbHelper.updatePolicy(policy); - } - }); - holder.delete.setOnClickListener(v -> { - DialogInterface.OnClickListener l = (dialog, which) -> { - policyList.remove(position); - notifyItemRemoved(position); - notifyItemRangeChanged(position, policyList.size()); - SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName), - Snackbar.LENGTH_SHORT).show(); - dbHelper.deletePolicy(policy); - }; - if (FingerprintHelper.useFingerprint()) { - new FingerprintAuthDialog((Activity) v.getContext(), - () -> l.onClick(null, 0)).show(); - } else { - new CustomAlertDialog(v.getContext()) - .setTitle(R.string.su_revoke_title) - .setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName)) - .setPositiveButton(R.string.yes, l) - .setNegativeButton(R.string.no_thanks, null) - .setCancelable(true) - .show(); - } - }); - } - - @Override - public int getItemCount() { - return policyList.size(); - } - - static class ViewHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.app_name) TextView appName; - @BindView(R.id.package_name) TextView packageName; - @BindView(R.id.app_icon) ImageView appIcon; - @BindView(R.id.master_switch) SwitchCompat masterSwitch; - @BindView(R.id.notification_switch) SwitchCompat notificationSwitch; - @BindView(R.id.logging_switch) SwitchCompat loggingSwitch; - @BindView(R.id.expand_layout) ViewGroup expandLayout; - @BindView(R.id.arrow) ImageView arrow; - @BindView(R.id.trigger) View trigger; - @BindView(R.id.delete) ImageView delete; - @BindView(R.id.more_info) ImageView moreInfo; - - Expandable settings; - - public ViewHolder(View itemView) { - super(itemView); - new PolicyAdapter$ViewHolder_ViewBinding(this, itemView); - settings = new ArrowExpandable(new ExpandableViewHolder(expandLayout), arrow); - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/ReposAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/ReposAdapter.java deleted file mode 100644 index 3b8cc7289..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/ReposAdapter.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.os.Build; -import android.text.TextUtils; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.SearchView; -import android.widget.TextView; - -import com.topjohnwu.magisk.App; -import com.topjohnwu.magisk.ClassMap; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.data.database.RepoDatabaseHelper; -import com.topjohnwu.magisk.model.download.DownloadModuleService; -import com.topjohnwu.magisk.model.entity.Module; -import com.topjohnwu.magisk.model.entity.Repo; -import com.topjohnwu.magisk.ui.base.IBaseLeanback; -import com.topjohnwu.magisk.ui.base.MagiskActivity; -import com.topjohnwu.magisk.utils.Event; -import com.topjohnwu.magisk.view.MarkDownWindow; -import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import java9.util.stream.StreamSupport; - -public class ReposAdapter - extends SectionedAdapter - implements Event.AutoListener, SearchView.OnQueryTextListener { - - private static final int UPDATES = 0; - private static final int INSTALLED = 1; - private static final int OTHERS = 2; - - private Map moduleMap; - private final RepoDatabaseHelper repoDB; - private final List>> repoPairs; - private List fullList; - private SearchView mSearch; - - public ReposAdapter() { - repoDB = App.self.getRepoDB(); - moduleMap = Collections.emptyMap(); - fullList = Collections.emptyList(); - repoPairs = new ArrayList<>(); - } - - @Override - public int getSectionCount() { - return repoPairs.size(); - } - - @Override - public int getItemCount(int section) { - return repoPairs.get(section).second.size(); - } - - @Override - public SectionHolder onCreateSectionViewHolder(ViewGroup parent) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.section, parent, false); - return new SectionHolder(v); - } - - @Override - public RepoHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false); - return new RepoHolder(v); - } - - @Override - public void onBindSectionViewHolder(SectionHolder holder, int section) { - switch (repoPairs.get(section).first) { - case UPDATES: - holder.sectionText.setText(R.string.update_available); - break; - case INSTALLED: - holder.sectionText.setText(R.string.installed); - break; - case OTHERS: - holder.sectionText.setText(R.string.not_installed); - break; - } - } - - @Override - public void onBindItemViewHolder(RepoHolder holder, int section, int position) { - Repo repo = repoPairs.get(section).second.get(position); - Context context = holder.itemView.getContext(); - - String name = repo.getName(); - String version = repo.getVersion(); - String author = repo.getAuthor(); - String description = repo.getDescription(); - String noInfo = context.getString(R.string.no_info_provided); - - holder.title.setText(TextUtils.isEmpty(name) ? noInfo : name); - holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version); - holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author)); - holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description); - holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString())); - - holder.infoLayout.setOnClickListener(v -> - MarkDownWindow.show(context, null, repo.getDetailUrl())); - - holder.downloadImage.setOnClickListener(v -> { - new CustomAlertDialog(context) - .setTitle(context.getString(R.string.repo_install_title, repo.getName())) - .setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename())) - .setCancelable(true) - .setPositiveButton(R.string.install, (d, i) -> - startDownload((MagiskActivity) context, repo, true)) - .setNeutralButton(R.string.download, (d, i) -> - startDownload((MagiskActivity) context, repo, false)) - .setNegativeButton(R.string.no_thanks, null) - .show(); - }); - } - - private void startDownload(Ctxt activity, Repo repo, Boolean install) { - activity.runWithExternalRW(() -> { - Intent intent = new Intent(activity, ClassMap.get(DownloadModuleService.class)) - .putExtra("repo", repo).putExtra("install", install); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - activity.startForegroundService(intent); - } else { - activity.startService(intent); - } - }); - } - - private void updateLists() { - if (mSearch != null) - onQueryTextChange(mSearch.getQuery().toString()); - else - onQueryTextChange(""); - } - - private static boolean noCaseContain(String a, String b) { - return a.toLowerCase().contains(b.toLowerCase()); - } - - public void setSearchView(SearchView view) { - mSearch = view; - mSearch.setOnQueryTextListener(this); - } - - public void notifyDBChanged(boolean refresh) { - try (Cursor c = repoDB.getRepoCursor()) { - fullList = new ArrayList<>(c.getCount()); - while (c.moveToNext()) - fullList.add(new Repo(c)); - } - if (refresh) - updateLists(); - } - - @Override - public void onEvent(int event) { - moduleMap = Event.getResult(event); - updateLists(); - } - - @Override - public int[] getListeningEvents() { - return new int[] {Event.MODULE_LOAD_DONE}; - } - - @Override - public boolean onQueryTextSubmit(String query) { - return false; - } - - @Override - public boolean onQueryTextChange(String s) { - List updates = new ArrayList<>(); - List installed = new ArrayList<>(); - List others = new ArrayList<>(); - - StreamSupport.stream(fullList) - .filter(repo -> noCaseContain(repo.getName(), s) - || noCaseContain(repo.getAuthor(), s) - || noCaseContain(repo.getDescription(), s)) - .forEach(repo -> { - Module module = moduleMap.get(repo.getId()); - if (module != null) { - if (repo.getVersionCode() > module.getVersionCode()) { - // Updates - updates.add(repo); - } else { - installed.add(repo); - } - } else { - others.add(repo); - } - }); - - repoPairs.clear(); - if (!updates.isEmpty()) - repoPairs.add(new Pair<>(UPDATES, updates)); - if (!installed.isEmpty()) - repoPairs.add(new Pair<>(INSTALLED, installed)); - if (!others.isEmpty()) - repoPairs.add(new Pair<>(OTHERS, others)); - - notifyDataSetChanged(); - return false; - } - - static class SectionHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.section_text) TextView sectionText; - - SectionHolder(View itemView) { - super(itemView); - new ReposAdapter$SectionHolder_ViewBinding(this, itemView); - } - } - - static class RepoHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.title) TextView title; - @BindView(R.id.version_name) TextView versionName; - @BindView(R.id.description) TextView description; - @BindView(R.id.author) TextView author; - @BindView(R.id.info_layout) View infoLayout; - @BindView(R.id.download) ImageView downloadImage; - @BindView(R.id.update_time) TextView updateTime; - - RepoHolder(View itemView) { - super(itemView); - new ReposAdapter$RepoHolder_ViewBinding(this, itemView); - } - - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/SectionedAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/SectionedAdapter.java deleted file mode 100644 index e04ec20c7..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/SectionedAdapter.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -public abstract class SectionedAdapter - extends RecyclerView.Adapter { - - private static final int SECTION_TYPE = Integer.MIN_VALUE; - - @NonNull - @Override - final public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - if (viewType == SECTION_TYPE) - return onCreateSectionViewHolder(parent); - return onCreateItemViewHolder(parent, viewType); - } - - @Override - @SuppressWarnings("unchecked") - final public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { - PositionInfo info = getPositionInfo(position); - if (info.position == -1) - onBindSectionViewHolder((S) holder, info.section); - else - onBindItemViewHolder((C) holder, info.section, info.position); - } - - @Override - final public int getItemCount() { - int size, sec; - size = sec = getSectionCount(); - for (int i = 0; i < sec; ++i){ - size += getItemCount(i); - } - return size; - } - - @Override - final public int getItemViewType(int position) { - PositionInfo info = getPositionInfo(position); - if (info.position == -1) - return SECTION_TYPE; - else - return getItemViewType(info.section, info.position); - } - - public int getItemViewType(int section, int position) { - return 0; - } - - protected int getSectionPosition(int section) { - return getItemPosition(section, -1); - } - - protected int getItemPosition(int section, int position) { - int realPosition = 0; - // Previous sections - for (int i = 0; i < section; ++i) { - realPosition += getItemCount(i) + 1; - } - // Current section - realPosition += position + 1; - return realPosition; - } - - private PositionInfo getPositionInfo(int position) { - int section = 0; - while (true) { - if (position == 0) - return new PositionInfo(section, -1); - position -= 1; - if (position < getItemCount(section)) - return new PositionInfo(section, position); - position -= getItemCount(section++); - } - } - - private static class PositionInfo { - int section; - int position; - PositionInfo(int section, int position) { - this.section = section; - this.position = position; - } - } - - public abstract int getSectionCount(); - public abstract int getItemCount(int section); - public abstract S onCreateSectionViewHolder(ViewGroup parent); - public abstract C onCreateItemViewHolder(ViewGroup parent, int viewType); - public abstract void onBindSectionViewHolder(S holder, int section); - public abstract void onBindItemViewHolder(C holder, int section, int position); -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/SuLogAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/SuLogAdapter.java deleted file mode 100644 index b1afdd51d..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/SuLogAdapter.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.RotateAnimation; -import android.widget.ImageView; -import android.widget.TextView; - -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.data.database.MagiskDB; -import com.topjohnwu.magisk.model.entity.SuLogEntry; -import com.topjohnwu.magisk.view.ExpandableViewHolder; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; - -public class SuLogAdapter extends SectionedAdapter { - - private List> logEntries; - private Set itemExpanded, sectionExpanded; - private MagiskDB suDB; - - public SuLogAdapter(MagiskDB db) { - suDB = db; - logEntries = Collections.emptyList(); - sectionExpanded = new HashSet<>(); - itemExpanded = new HashSet<>(); - } - - @Override - public int getSectionCount() { - return logEntries.size(); - } - - @Override - public int getItemCount(int section) { - return sectionExpanded.contains(section) ? logEntries.get(section).size() : 0; - } - - @Override - public SectionHolder onCreateSectionViewHolder(ViewGroup parent) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false); - return new SectionHolder(v); - } - - @Override - public LogViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false); - return new LogViewHolder(v); - } - - @Override - public void onBindSectionViewHolder(SectionHolder holder, int section) { - SuLogEntry entry = logEntries.get(section).get(0); - holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0); - holder.itemView.setOnClickListener(v -> { - RotateAnimation rotate; - if (sectionExpanded.contains(section)) { - holder.arrow.setRotation(0); - rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - sectionExpanded.remove(section); - notifyItemRangeRemoved(getItemPosition(section, 0), logEntries.get(section).size()); - } else { - rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - sectionExpanded.add(section); - notifyItemRangeInserted(getItemPosition(section, 0), logEntries.get(section).size()); - } - rotate.setDuration(300); - rotate.setFillAfter(true); - holder.arrow.setAnimation(rotate); - }); - holder.date.setText(entry.getDateString()); - } - - @Override - public void onBindItemViewHolder(LogViewHolder holder, int section, int position) { - SuLogEntry entry = logEntries.get(section).get(position); - int realIdx = getItemPosition(section, position); - holder.expandable.setExpanded(itemExpanded.contains(realIdx)); - holder.itemView.setOnClickListener(view -> { - if (holder.expandable.isExpanded()) { - holder.expandable.collapse(); - itemExpanded.remove(realIdx); - } else { - holder.expandable.expand(); - itemExpanded.add(realIdx); - } - }); - Context context = holder.itemView.getContext(); - holder.appName.setText(entry.appName); - holder.action.setText(entry.action ? R.string.grant : R.string.deny); - holder.pid.setText(context.getString(R.string.pid, entry.fromPid)); - holder.uid.setText(context.getString(R.string.target_uid, entry.toUid)); - holder.command.setText(context.getString(R.string.command, entry.command)); - holder.time.setText(entry.getTimeString()); - } - - public void notifyDBChanged() { - logEntries = suDB.getLogs(); - itemExpanded.clear(); - sectionExpanded.clear(); - sectionExpanded.add(0); - notifyDataSetChanged(); - } - - static class SectionHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.date) TextView date; - @BindView(R.id.arrow) ImageView arrow; - - SectionHolder(View itemView) { - super(itemView); - new SuLogAdapter$SectionHolder_ViewBinding(this, itemView); - } - } - - static class LogViewHolder extends RecyclerView.ViewHolder { - - @BindView(R.id.app_name) TextView appName; - @BindView(R.id.action) TextView action; - @BindView(R.id.time) TextView time; - @BindView(R.id.pid) TextView pid; - @BindView(R.id.uid) TextView uid; - @BindView(R.id.cmd) TextView command; - @BindView(R.id.expand_layout) ViewGroup expandLayout; - - ExpandableViewHolder expandable; - - LogViewHolder(View itemView) { - super(itemView); - new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView); - expandable = new ExpandableViewHolder(expandLayout); - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/adapters/TabFragmentAdapter.java b/app/src/main/java/com/topjohnwu/magisk/model/adapters/TabFragmentAdapter.java deleted file mode 100644 index eddb897fb..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/adapters/TabFragmentAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.topjohnwu.magisk.model.adapters; - - -import java.util.ArrayList; -import java.util.List; - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; - -public class TabFragmentAdapter extends FragmentPagerAdapter { - - private List fragmentList; - private List titleList; - - public TabFragmentAdapter(FragmentManager fm) { - super(fm); - fragmentList = new ArrayList<>(); - titleList = new ArrayList<>(); - } - - @Override - public Fragment getItem(int position) { - return fragmentList.get(position); - } - - @Override - public int getCount() { - return fragmentList.size(); - } - - @Override - public CharSequence getPageTitle(int position) { - return titleList.get(position); - } - - public void addTab(Fragment fragment, String title) { - fragmentList.add(fragment); - titleList.add(title); - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/MagiskLogFragment.java b/app/src/main/java/com/topjohnwu/magisk/ui/log/MagiskLogFragment.java deleted file mode 100644 index 0e44c25e9..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/MagiskLogFragment.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.topjohnwu.magisk.ui.log; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.google.android.material.snackbar.Snackbar; -import com.topjohnwu.magisk.Const; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.model.adapters.StringListAdapter; -import com.topjohnwu.magisk.ui.base.BaseFragment; -import com.topjohnwu.magisk.utils.Utils; -import com.topjohnwu.magisk.view.SnackbarMaker; -import com.topjohnwu.superuser.Shell; -import com.topjohnwu.superuser.internal.NOPList; - -import java.io.File; -import java.io.IOException; -import java.util.Calendar; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; - -public class MagiskLogFragment extends BaseFragment { - - @BindView(R.id.recyclerView) RecyclerView rv; - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_magisk_log, container, false); - unbinder = new MagiskLogFragment_ViewBinding(this, view); - setHasOptionsMenu(true); - return view; - } - - @Override - public void onStart() { - super.onStart(); - getActivity().setTitle(R.string.log); - } - - @Override - public void onResume() { - super.onResume(); - readLogs(); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_log, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_refresh: - readLogs(); - return true; - case R.id.menu_save: - runWithExternalRW(this::saveLogs); - return true; - case R.id.menu_clear: - clearLogs(); - rv.setAdapter(new MagiskLogAdapter(NOPList.getInstance())); - return true; - default: - return true; - } - } - - private void readLogs() { - Shell.su("tail -n 5000 " + Const.MAGISK_LOG).submit(result -> { - rv.setAdapter(new MagiskLogAdapter(result.getOut())); - }); - } - - private void saveLogs() { - Calendar now = Calendar.getInstance(); - String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log", - now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1, - now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), - now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); - - File logFile = new File(Const.EXTERNAL_PATH, filename); - try { - logFile.createNewFile(); - } catch (IOException e) { - return; - } - Shell.su("cat " + Const.MAGISK_LOG + " > " + logFile) - .submit(result -> - SnackbarMaker.make(rv, logFile.getPath(), Snackbar.LENGTH_SHORT).show()); - } - - private void clearLogs() { - Shell.su("echo -n > " + Const.MAGISK_LOG).submit(); - SnackbarMaker.make(rv, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); - } - - private class MagiskLogAdapter extends StringListAdapter { - - MagiskLogAdapter(List list) { - super(list); - if (mList.isEmpty()) - mList.add(requireContext().getString(R.string.log_is_empty)); - } - - @Override - protected int itemLayoutRes() { - return R.layout.list_item_console; - } - - @NonNull - @Override - public ViewHolder createViewHolder(@NonNull View v) { - return new ViewHolder(v); - } - - @Override - protected void onUpdateTextWidth(ViewHolder holder) { - super.onUpdateTextWidth(holder); - // Find the longest string and update accordingly - int max = 0; - String maxStr = ""; - for (String s : mList) { - int len = s.length(); - if (len > max) { - max = len; - maxStr = s; - } - } - holder.txt.setText(maxStr); - super.onUpdateTextWidth(holder); - } - - public class ViewHolder extends StringListAdapter.ViewHolder { - - public ViewHolder(@NonNull View itemView) { - super(itemView); - } - - @Override - protected int textViewResId() { - return R.id.txt; - } - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/SuLogFragment.java b/app/src/main/java/com/topjohnwu/magisk/ui/log/SuLogFragment.java deleted file mode 100644 index 07e42adaf..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/SuLogFragment.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.topjohnwu.magisk.ui.log; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.model.adapters.SuLogAdapter; -import com.topjohnwu.magisk.ui.base.BaseFragment; - -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; - -public class SuLogFragment extends BaseFragment { - - @BindView(R.id.empty_rv) TextView emptyRv; - @BindView(R.id.recyclerView) RecyclerView recyclerView; - - private SuLogAdapter adapter; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_log, menu); - menu.findItem(R.id.menu_save).setVisible(false); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - View v = inflater.inflate(R.layout.fragment_su_log, container, false); - unbinder = new SuLogFragment_ViewBinding(this, v); - adapter = new SuLogAdapter(app.getDB()); - recyclerView.setAdapter(adapter); - - updateList(); - - return v; - } - - private void updateList() { - adapter.notifyDBChanged(); - - if (adapter.getSectionCount() == 0) { - emptyRv.setVisibility(View.VISIBLE); - recyclerView.setVisibility(View.GONE); - } else { - emptyRv.setVisibility(View.GONE); - recyclerView.setVisibility(View.VISIBLE); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_refresh: - updateList(); - return true; - case R.id.menu_clear: - app.getDB().clearLogs(); - updateList(); - return true; - default: - return true; - } - } -}