Improve MagiskHide app listing

- Prevent platform apps from showing up
- Add new option to toggle whether to show system apps
This commit is contained in:
topjohnwu 2019-01-31 23:40:33 -05:00
parent a60710e3bb
commit da13b5dbf2
5 changed files with 104 additions and 58 deletions

View File

@ -66,6 +66,7 @@ public class Config {
public static final String DARK_THEME = "dark_theme"; public static final String DARK_THEME = "dark_theme";
public static final String ETAG_KEY = "ETag"; public static final String ETAG_KEY = "ETag";
public static final String REPO_ORDER = "repo_order"; public static final String REPO_ORDER = "repo_order";
public static final String SHOW_SYSTEM_APP = "show_system";
} }
public static class Value { public static class Value {
@ -108,16 +109,17 @@ public class Config {
// prefs bool // prefs bool
defs.putBoolean(Key.CHECK_UPDATES, true); defs.putBoolean(Key.CHECK_UPDATES, true);
// defs.putBoolean(Const.Key.DARK_THEME, false); // defs.putBoolean(Key.DARK_THEME, false);
// defs.putBoolean(Const.Key.SU_REAUTH, false); // defs.putBoolean(Key.SU_REAUTH, false);
// defs.putBoolean(Const.Key.MAGISKHIDE, false); // defs.putBoolean(Key.MAGISKHIDE, false);
// defs.putBoolean(Const.Key.COREONLY, false); // defs.putBoolean(Key.COREONLY, false);
// defs.putBoolean(Key.SHOW_SYSTEM_APP, false);
// prefs string // prefs string
defs.putString(Key.CUSTOM_CHANNEL, ""); defs.putString(Key.CUSTOM_CHANNEL, "");
defs.putString(Key.BOOT_FORMAT, ".img"); defs.putString(Key.BOOT_FORMAT, ".img");
defs.putString(Key.LOCALE, ""); defs.putString(Key.LOCALE, "");
// defs.putString(Const.Key.ETAG_KEY, null); // defs.putString(Key.ETAG_KEY, null);
// db int // db int
defs.putInt(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB); defs.putInt(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB);
@ -125,10 +127,10 @@ public class Config {
defs.putInt(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY); defs.putInt(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY);
// db bool // db bool
// defs.putBoolean(Const.Key.SU_FINGERPRINT, false); // defs.putBoolean(Key.SU_FINGERPRINT, false);
// db strings // db strings
// defs.putString(Const.Key.SU_MANAGER, null); // defs.putString(Key.SU_MANAGER, null);
} }
public static void loadMagiskInfo() { public static void loadMagiskInfo() {
@ -245,6 +247,7 @@ public class Config {
case Key.CHECK_UPDATES: case Key.CHECK_UPDATES:
case Key.MAGISKHIDE: case Key.MAGISKHIDE:
case Key.COREONLY: case Key.COREONLY:
case Key.SHOW_SYSTEM_APP:
return PREF_BOOL; return PREF_BOOL;
case Key.CUSTOM_CHANNEL: case Key.CUSTOM_CHANNEL:

View File

@ -2,14 +2,13 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@ -18,6 +17,7 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -25,22 +25,30 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> { public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
private List<ApplicationInfo> fullList, showList; private static PackageInfo PLATFORM;
private List<PackageInfo> fullList, showList;
private List<String> hideList; private List<String> hideList;
private PackageManager pm; private PackageManager pm;
private ApplicationFilter filter; private boolean showSystem;
public ApplicationAdapter(Context context) { public ApplicationAdapter(Context context) {
fullList = showList = Collections.emptyList(); fullList = showList = Collections.emptyList();
hideList = Collections.emptyList(); hideList = Collections.emptyList();
filter = new ApplicationFilter();
pm = context.getPackageManager(); pm = context.getPackageManager();
AsyncTask.THREAD_POOL_EXECUTOR.execute(this::loadApps); showSystem = false;
if (PLATFORM == null) {
try {
PLATFORM = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException ignored) {}
}
AsyncTask.SERIAL_EXECUTOR.execute(this::loadApps);
} }
@NonNull @NonNull
@ -50,12 +58,17 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return new ViewHolder(v); return new ViewHolder(v);
} }
@WorkerThread
private void loadApps() { private void loadApps() {
fullList = pm.getInstalledApplications(0); fullList = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
hideList = Shell.su("magiskhide --ls").exec().getOut(); hideList = Shell.su("magiskhide --ls").exec().getOut();
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) { for (Iterator<PackageInfo> i = fullList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next(); PackageInfo info = i.next();
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) { if (Const.HIDE_BLACKLIST.contains(info.packageName) ||
/* Do not show disabled apps */
!info.applicationInfo.enabled ||
/* Never show platform apps */
PLATFORM.signatures[0].equals(info.signatures[0])) {
i.remove(); i.remove();
} }
} }
@ -63,7 +76,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
boolean ah = hideList.contains(a.packageName); boolean ah = hideList.contains(a.packageName);
boolean bh = hideList.contains(b.packageName); boolean bh = hideList.contains(b.packageName);
if (ah == bh) { if (ah == bh) {
return Utils.getAppLabel(a, pm).compareToIgnoreCase(Utils.getAppLabel(b, pm)); return Utils.getAppLabel(a.applicationInfo, pm)
.compareToIgnoreCase(Utils.getAppLabel(b.applicationInfo, pm));
} else if (ah) { } else if (ah) {
return -1; return -1;
} else { } else {
@ -73,9 +87,13 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
Topic.publish(false, Topic.MAGISK_HIDE_DONE); Topic.publish(false, Topic.MAGISK_HIDE_DONE);
} }
public void setShowSystem(boolean b) {
showSystem = b;
}
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ApplicationInfo info = showList.get(position); ApplicationInfo info = showList.get(position).applicationInfo;
holder.appIcon.setImageDrawable(info.loadIcon(pm)); holder.appIcon.setImageDrawable(info.loadIcon(pm));
holder.appName.setText(Utils.getAppLabel(info, pm)); holder.appName.setText(Utils.getAppLabel(info, pm));
@ -99,8 +117,37 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return showList.size(); return showList.size();
} }
private boolean contains(String s, String filter) {
return s.toLowerCase().contains(filter);
}
// Show if have launch intent or not system app
private boolean systemFilter(PackageInfo info) {
if (showSystem)
return true;
return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
pm.getLaunchIntentForPackage(info.packageName) != null;
}
public void filter(String constraint) { public void filter(String constraint) {
filter.filter(constraint); AsyncTask.SERIAL_EXECUTOR.execute(() -> {
showList = new ArrayList<>();
if (constraint == null || constraint.length() == 0) {
for (PackageInfo info : fullList) {
if (systemFilter(info))
showList.add(info);
}
} else {
String filter = constraint.toLowerCase();
for (PackageInfo info : fullList) {
if ((contains(Utils.getAppLabel(info.applicationInfo, pm), filter) ||
contains(info.packageName, filter)) && systemFilter(info)) {
showList.add(info);
}
}
}
UiThreadHandler.run(this::notifyDataSetChanged);
});
} }
public void refresh() { public void refresh() {
@ -119,33 +166,4 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
new ApplicationAdapter$ViewHolder_ViewBinding(this, itemView); new ApplicationAdapter$ViewHolder_ViewBinding(this, itemView);
} }
} }
class ApplicationFilter extends Filter {
private boolean lowercaseContains(String s, CharSequence filter) {
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint == null || constraint.length() == 0) {
showList = fullList;
} else {
showList = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : fullList) {
if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|| lowercaseContains(info.packageName, filter)) {
showList.add(info);
}
}
}
return null;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
}
} }

View File

@ -4,10 +4,12 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.SearchView; import android.widget.SearchView;
import com.topjohnwu.magisk.Config;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ApplicationAdapter; import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.BaseFragment; import com.topjohnwu.magisk.components.BaseFragment;
@ -23,10 +25,9 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView; @BindView(R.id.recyclerView) RecyclerView recyclerView;
SearchView search;
private ApplicationAdapter appAdapter;
private SearchView search;
private ApplicationAdapter adapter;
private SearchView.OnQueryTextListener searchListener; private SearchView.OnQueryTextListener searchListener;
@Override @Override
@ -41,22 +42,22 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false); View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
unbinder = new MagiskHideFragment_ViewBinding(this, view); unbinder = new MagiskHideFragment_ViewBinding(this, view);
appAdapter = new ApplicationAdapter(requireActivity()); adapter = new ApplicationAdapter(requireActivity());
recyclerView.setAdapter(appAdapter); recyclerView.setAdapter(adapter);
mSwipeRefreshLayout.setRefreshing(true); mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(appAdapter::refresh); mSwipeRefreshLayout.setOnRefreshListener(adapter::refresh);
searchListener = new SearchView.OnQueryTextListener() { searchListener = new SearchView.OnQueryTextListener() {
@Override @Override
public boolean onQueryTextSubmit(String query) { public boolean onQueryTextSubmit(String query) {
appAdapter.filter(query); adapter.filter(query);
return false; return false;
} }
@Override @Override
public boolean onQueryTextChange(String newText) { public boolean onQueryTextChange(String newText) {
appAdapter.filter(newText); adapter.filter(newText);
return false; return false;
} }
}; };
@ -71,6 +72,21 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
inflater.inflate(R.menu.menu_magiskhide, menu); inflater.inflate(R.menu.menu_magiskhide, menu);
search = (SearchView) menu.findItem(R.id.app_search).getActionView(); search = (SearchView) menu.findItem(R.id.app_search).getActionView();
search.setOnQueryTextListener(searchListener); search.setOnQueryTextListener(searchListener);
boolean showSystem = Config.get(Config.Key.SHOW_SYSTEM_APP);
menu.findItem(R.id.show_system).setChecked(showSystem);
adapter.setShowSystem(showSystem);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.show_system) {
boolean showSystem = !item.isChecked();
item.setChecked(showSystem);
Config.set(Config.Key.SHOW_SYSTEM_APP, showSystem);
adapter.setShowSystem(showSystem);
adapter.filter(search.getQuery().toString());
}
return true;
} }
@Override @Override
@ -81,6 +97,6 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
@Override @Override
public void onPublish(int topic, Object[] result) { public void onPublish(int topic, Object[] result) {
mSwipeRefreshLayout.setRefreshing(false); mSwipeRefreshLayout.setRefreshing(false);
appAdapter.filter(search.getQuery().toString()); adapter.filter(search.getQuery().toString());
} }
} }

View File

@ -7,4 +7,10 @@
android:title="" android:title=""
app:actionViewClass="android.widget.SearchView" app:actionViewClass="android.widget.SearchView"
app:showAsAction="always"/> app:showAsAction="always"/>
<item
android:id="@+id/show_system"
android:title="@string/show_system_app"
android:checkable="true"/>
</menu> </menu>

View File

@ -232,4 +232,7 @@
<string name="target_uid">Target UID: %1$d</string> <string name="target_uid">Target UID: %1$d</string>
<string name="command">Command: %1$s</string> <string name="command">Command: %1$s</string>
<!-- MagiskHide -->
<string name="show_system_app">Show system apps</string>
</resources> </resources>