Skip to content

Commit

Permalink
[Main] Add options for filtering
Browse files Browse the repository at this point in the history
Add the ability to filter apps persistently (ie. configurations are
preserved across launch or update). The filter options are: no
filter (every app info is shown), filter user apps, system apps,
disabled apps and apps with blocked components (aka, apps with
rules).
  • Loading branch information
MuntashirAkon committed Jul 29, 2020
1 parent 4cd3371 commit 78d9f6b
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public class MainActivity extends AppCompatActivity implements
private static final int REQUEST_CODE_BATCH_EXPORT = 441;

/**
* A list of packages separated by \r\n. Debug apps should have a * after their package names.
* A list of packages separated by \r\n.
*/
public static String packageList;
/**
Expand Down Expand Up @@ -127,6 +127,20 @@ public class MainActivity extends AppCompatActivity implements
public static final int SORT_BY_DISABLED_APP = 7;
public static final int SORT_BY_BLOCKED_COMPONENTS = 8;

@IntDef(flag = true, value = {
FILTER_NO_FILTER,
FILTER_USER_APPS,
FILTER_SYSTEM_APPS,
FILTER_DISABLED_APPS,
FILTER_APPS_WITH_RULES
})
public @interface Filter {}
public static final int FILTER_NO_FILTER = 0;
public static final int FILTER_USER_APPS = 1;
public static final int FILTER_SYSTEM_APPS = 1 << 1;
public static final int FILTER_DISABLED_APPS = 1 << 2;
public static final int FILTER_APPS_WITH_RULES = 1 << 3;

private MainActivity.MainRecyclerAdapter mAdapter;
private List<ApplicationItem> mApplicationItems = new ArrayList<>();
private SearchView mSearchView;
Expand Down Expand Up @@ -320,6 +334,18 @@ public boolean onCreateOptionsMenu(Menu menu) {
public boolean onPrepareOptionsMenu(@NonNull Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(sSortMenuItemIdsMap[mSortBy]).setChecked(true);
if (mModel != null) {
int flags = mModel.getFilterFlags();
if ((flags & MainActivity.FILTER_USER_APPS) != 0) {
menu.findItem(R.id.action_filter_user_apps).setChecked(true);
} else if ((flags & MainActivity.FILTER_SYSTEM_APPS) != 0) {
menu.findItem(R.id.action_filter_system_apps).setChecked(true);
} else if ((flags & MainActivity.FILTER_DISABLED_APPS) != 0) {
menu.findItem(R.id.action_filter_disabled_apps).setChecked(true);
} else if ((flags & MainActivity.FILTER_APPS_WITH_RULES) != 0) {
menu.findItem(R.id.action_filter_apps_with_rules).setChecked(true);
}
}
return true;
}

Expand Down Expand Up @@ -350,6 +376,7 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivity(settingsIntent);
return true;
// Sort
case R.id.action_sort_by_app_label:
setSortBy(SORT_BY_APP_LABEL);
item.setChecked(true);
Expand Down Expand Up @@ -386,6 +413,27 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
setSortBy(SORT_BY_BLOCKED_COMPONENTS);
item.setChecked(true);
return true;
// Filter
case R.id.action_filter_user_apps:
if (!item.isChecked()) mModel.addFilterFlag(FILTER_USER_APPS);
else mModel.removeFilterFlag(FILTER_USER_APPS);
item.setChecked(!item.isChecked());
return true;
case R.id.action_filter_system_apps:
if (!item.isChecked()) mModel.addFilterFlag(FILTER_SYSTEM_APPS);
else mModel.removeFilterFlag(FILTER_SYSTEM_APPS);
item.setChecked(!item.isChecked());
return true;
case R.id.action_filter_disabled_apps:
if (!item.isChecked()) mModel.addFilterFlag(FILTER_DISABLED_APPS);
else mModel.removeFilterFlag(FILTER_DISABLED_APPS);
item.setChecked(!item.isChecked());
return true;
case R.id.action_filter_apps_with_rules:
if (!item.isChecked()) mModel.addFilterFlag(FILTER_APPS_WITH_RULES);
else mModel.removeFilterFlag(FILTER_APPS_WITH_RULES);
item.setChecked(!item.isChecked());
return true;
case R.id.action_app_usage:
Intent usageIntent = new Intent(this, AppUsageActivity.class);
startActivity(usageIntent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public class ApplicationItem {
* Blocked components count
*/
public Integer blockedCount = 0;
/**
* Whether the item is a user app (or system app)
*/
public boolean isUser;
/**
* Whether the app is disabled
*/
public boolean isDisabled;
/**
* Whether the item is selected
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum PrefKey {
PREF_ENABLE_KILL_FOR_SYSTEM_BOOL,
PREF_GLOBAL_BLOCKING_ENABLED_BOOL,
PREF_LAST_VERSION_CODE_LONG,
PREF_MAIN_WINDOW_FILTER_FLAGS_INT,
PREF_MAIN_WINDOW_SORT_ORDER_INT,
PREF_ROOT_MODE_ENABLED_BOOL,
PREF_USAGE_ACCESS_ENABLED_BOOL;
Expand Down Expand Up @@ -170,6 +171,7 @@ private void init() {
case PREF_GLOBAL_BLOCKING_ENABLED_BOOL: return false;
case PREF_LAST_VERSION_CODE_LONG: return 0L;
case PREF_APP_THEME_INT: return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
case PREF_MAIN_WINDOW_FILTER_FLAGS_INT: return MainActivity.FILTER_NO_FILTER;
case PREF_MAIN_WINDOW_SORT_ORDER_INT: return MainActivity.SORT_BY_APP_LABEL;
}
throw new IllegalArgumentException("Pref key not found.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,19 @@ public class MainViewModel extends AndroidViewModel {
private PackageIntentReceiver mPackageObserver;
private Handler mHandler;
private @MainActivity.SortOrder int mSortBy;
private @MainActivity.Filter int mFilterFlags;
private String searchQuery;
private List<String> selectedPackages = new LinkedList<>();
private List<ApplicationItem> selectedApplicationItems = new LinkedList<>();
private int flagSigningInfo;
public MainViewModel(@NonNull Application application) {
super(application);
Log.d("MVM", "New instance created");
mPackageManager = application.getPackageManager();
mHandler = new Handler(application.getMainLooper());
mPackageObserver = new PackageIntentReceiver(this);
mSortBy = (int) AppPref.get(AppPref.PrefKey.PREF_MAIN_WINDOW_SORT_ORDER_INT);
mFilterFlags = (int) AppPref.get(AppPref.PrefKey.PREF_MAIN_WINDOW_FILTER_FLAGS_INT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
flagSigningInfo = PackageManager.GET_SIGNING_CERTIFICATES;
else flagSigningInfo = PackageManager.GET_SIGNATURES;
Expand Down Expand Up @@ -118,38 +121,60 @@ public String getSearchQuery() {

public void setSearchQuery(String searchQuery) {
this.searchQuery = searchQuery;
new Thread(this::filterItems).start();
new Thread(this::filterItemsByFlags).start();
}

public void setSortBy(int sortBy) {
if (mSortBy != sortBy) {
new Thread(() -> {
sortApplicationList(sortBy);
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
filterItemsByFlags();
}).start();
}
mSortBy = sortBy;
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_MAIN_WINDOW_SORT_ORDER_INT, mSortBy);
}

public int getFilterFlags() {
return mFilterFlags;
}

public void addFilterFlag(@MainActivity.Filter int filterFlag) {
mFilterFlags |= filterFlag;
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_MAIN_WINDOW_FILTER_FLAGS_INT, mFilterFlags);
new Thread(() -> {
synchronized (applicationItems) {
filterItemsByFlags();
}
}).start();
}

public void removeFilterFlag(@MainActivity.Filter int filterFlag) {
mFilterFlags &= ~filterFlag;
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_MAIN_WINDOW_FILTER_FLAGS_INT, mFilterFlags);
new Thread(() -> {
synchronized (applicationItems) {
filterItemsByFlags();
}
}).start();
}

@SuppressLint("PackageManagerGetSignatures")
public void loadApplicationItems() {
new Thread(() -> {
synchronized (applicationItems) {
String pName;
applicationItems.clear();
if (MainActivity.packageList != null) {
String[] aList = MainActivity.packageList.split("[\\r\\n]+");
for (String s : aList) {
String[] packageList = MainActivity.packageList.split("[\\r\\n]+");
for (String packageName : packageList) {
ApplicationItem item = new ApplicationItem();
if (s.endsWith("*")) {
item.star = true;
pName = s.substring(0, s.length() - 1);
} else pName = s;
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(pName, PackageManager.GET_META_DATA | flagSigningInfo);
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA | flagSigningInfo);
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
item.applicationInfo = applicationInfo;
item.star = (applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
item.isUser = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0;
item.isDisabled = !applicationInfo.enabled;
item.label = applicationInfo.loadLabel(mPackageManager).toString();
item.date = packageInfo.lastUpdateTime; // .firstInstallTime;
item.sha = Utils.getIssuerAndAlg(packageInfo);
Expand All @@ -164,7 +189,9 @@ public void loadApplicationItems() {
for (ApplicationInfo applicationInfo : applicationInfoList) {
ApplicationItem item = new ApplicationItem();
item.applicationInfo = applicationInfo;
item.star = ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
item.star = (applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
item.isUser = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0;
item.isDisabled = !applicationInfo.enabled;
item.label = applicationInfo.loadLabel(mPackageManager).toString();
if (Build.VERSION.SDK_INT >= 26) {
item.size = (long) -1 * applicationInfo.targetSdkVersion;
Expand All @@ -181,22 +208,14 @@ public void loadApplicationItems() {
applicationItems.add(item);
}
}
if (Build.VERSION.SDK_INT <= 25) loadPackageSize();
sortApplicationList(mSortBy);
if (!TextUtils.isEmpty(searchQuery)) {
if (Build.VERSION.SDK_INT <= 25) loadPackageSize();
filterItems();
} else {
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
if (Build.VERSION.SDK_INT <= 25) {
loadPackageSize();
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
}
}
filterItemsByFlags();
}
}).start();
}

private void filterItems() {
private void filterItemsByQuery(@NonNull List<ApplicationItem> applicationItems) {
List<ApplicationItem> filteredApplicationItems = new ArrayList<>();
for (ApplicationItem item: applicationItems) {
if (item.label.toLowerCase(Locale.ROOT).contains(searchQuery)
Expand All @@ -206,6 +225,37 @@ private void filterItems() {
mHandler.post(() -> applicationItemsLiveData.postValue(filteredApplicationItems));
}

private void filterItemsByFlags() {
if (mFilterFlags == MainActivity.FILTER_NO_FILTER) {
if (!TextUtils.isEmpty(searchQuery)) {
filterItemsByQuery(applicationItems);
} else {
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
}
} else {
List<ApplicationItem> filteredApplicationItems = new ArrayList<>();
if ((mFilterFlags & MainActivity.FILTER_APPS_WITH_RULES) != 0) {
loadBlockingRules();
}
for (ApplicationItem item : applicationItems) {
if ((mFilterFlags & MainActivity.FILTER_USER_APPS) != 0 && item.isUser) {
filteredApplicationItems.add(item);
} else if ((mFilterFlags & MainActivity.FILTER_SYSTEM_APPS) != 0 && !item.isUser) {
filteredApplicationItems.add(item);
} else if ((mFilterFlags & MainActivity.FILTER_DISABLED_APPS) != 0 && item.isDisabled) {
filteredApplicationItems.add(item);
} else if ((mFilterFlags & MainActivity.FILTER_APPS_WITH_RULES) != 0 && item.blockedCount > 0) {
filteredApplicationItems.add(item);
}
}
if (!TextUtils.isEmpty(searchQuery)) {
filterItemsByQuery(filteredApplicationItems);
} else {
mHandler.post(() -> applicationItemsLiveData.postValue(filteredApplicationItems));
}
}
}

private void loadBlockingRules() {
for (int i = 0; i<applicationItems.size(); ++i) {
ApplicationItem applicationItem = applicationItems.get(i);
Expand Down Expand Up @@ -302,15 +352,15 @@ private void updateInfoForPackages(@Nullable String[] packages, @NonNull String
removePackageFromApplicationItems(packageName);
}
}
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
filterItemsByFlags();
break;
case Intent.ACTION_PACKAGE_CHANGED:
for (String packageName: packages) {
ApplicationItem item = getNewApplicationItem(packageName);
if (item != null) insertApplicationItemInApplicationItems(item);
}
sortApplicationList(mSortBy);
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
filterItemsByFlags();
break;
case Intent.ACTION_PACKAGE_ADDED:
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
Expand All @@ -319,7 +369,7 @@ private void updateInfoForPackages(@Nullable String[] packages, @NonNull String
if (item != null) applicationItems.add(item);
}
sortApplicationList(mSortBy);
mHandler.post(() -> applicationItemsLiveData.postValue(applicationItems));
filterItemsByFlags();
}
}

Expand All @@ -344,7 +394,9 @@ private ApplicationItem getNewApplicationItem(String packageName) {
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
item.applicationInfo = applicationInfo;
item.label = applicationInfo.loadLabel(mPackageManager).toString();
item.star = ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
item.star = (applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
item.isUser = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0;
item.isDisabled = !applicationInfo.enabled;
item.date = packageInfo.lastUpdateTime; // .firstInstallTime;
item.sha = Utils.getIssuerAndAlg(packageInfo);
if (Build.VERSION.SDK_INT >= 26) {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_baseline_outlined_flag_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?colorAccent"
android:pathData="M14,6l-1,-2L5,4v17h2v-7h5l1,2h7L20,6h-6zM18,14h-4l-1,-2L7,12L7,6h5l1,2h5v6z"/>
</vector>
29 changes: 29 additions & 0 deletions app/src/main/res/menu/activity_main_actions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,35 @@
</menu>
</item>

<item
android:icon="@drawable/ic_baseline_outlined_flag_24"
android:title="@string/filter"
app:showAsAction="never">
<menu>
<group
android:checkableBehavior="all"
android:menuCategory="container">

<item
android:id="@+id/action_filter_user_apps"
android:title="@string/filter_user_apps" />

<item
android:id="@+id/action_filter_system_apps"
android:title="@string/filter_system_apps" />

<item
android:id="@+id/action_filter_disabled_apps"
android:title="@string/sort_by_package_name" />

<item
android:id="@+id/action_filter_apps_with_rules"
android:title="@string/filter_apps_with_rules" />

</group>
</menu>
</item>

<item
android:id="@+id/action_refresh"
android:icon="@drawable/ic_refresh_black_24dp"
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -567,4 +567,8 @@
<string name="termux">Termux</string>
<string name="package_name_is_installed_successfully">%s is installed successfully</string>
<string name="failed_to_install_package_name">Failed to install %s</string>
<string name="filter">Filter</string>
<string name="filter_user_apps">User apps</string>
<string name="filter_system_apps">System apps</string>
<string name="filter_apps_with_rules">Apps with rules</string>
</resources>
2 changes: 1 addition & 1 deletion app/version.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
VERSION_NAME=2.5.10
VERSION_CODE=322
VERSION_CODE=323

0 comments on commit 78d9f6b

Please sign in to comment.