Cleaned up unnecessary classes

This commit is contained in:
Viktor De Pasquale 2019-04-22 17:03:21 +02:00
parent aceb3ee863
commit 6df7006b36
9 changed files with 0 additions and 1481 deletions

View File

@ -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
<ApplicationAdapter.AppViewHolder, ApplicationAdapter.ProcessViewHolder> {
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<String> 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<String> DEFAULT_HIDELIST = Lists.of(
SAFETYNET_PROCESS
);
private static int BOTTOM_MARGIN = -1;
private List<HideAppInfo> fullList, showList;
private List<HideTarget> 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<HideAppInfo> 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<String> 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<String> 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<HideAppInfo> {
String name;
ApplicationInfo info;
List<HideProcessInfo> processList;
boolean expanded;
IndeterminateCheckBox.OnStateChangedListener listener;
HideAppInfo(ApplicationInfo appInfo, Set<String> 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<HideAppInfo> 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<HideProcessInfo> {
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<HideProcessInfo> 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);
}
}
}

View File

@ -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<ModulesAdapter.ViewHolder> {
private final List<Module> mList;
public ModulesAdapter(List<Module> 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);
}
}
}
}

View File

@ -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<PolicyAdapter.ViewHolder> {
private final List<Policy> policyList;
private final MagiskDB dbHelper;
private final PackageManager pm;
private final boolean[] expandList;
public PolicyAdapter(List<Policy> 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);
}
}
}

View File

@ -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<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder>
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<String, Module> moduleMap;
private final RepoDatabaseHelper repoDB;
private final List<Pair<Integer, List<Repo>>> repoPairs;
private List<Repo> 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 <Ctxt extends Context & IBaseLeanback> 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<Repo> updates = new ArrayList<>();
List<Repo> installed = new ArrayList<>();
List<Repo> 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);
}
}
}

View File

@ -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<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
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);
}

View File

@ -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<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
private List<List<SuLogEntry>> logEntries;
private Set<Integer> 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);
}
}
}

View File

@ -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<Fragment> fragmentList;
private List<String> 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);
}
}

View File

@ -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.ViewHolder> {
MagiskLogAdapter(List<String> 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;
}
}
}
}

View File

@ -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;
}
}
}