Skip to content

Commit

Permalink
feat: Option to display system apps in SetHideAppActivity (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
deltazefiro committed Jul 26, 2023
1 parent 7821902 commit 1c314b6
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 64 deletions.
26 changes: 5 additions & 21 deletions app/src/main/java/deltazero/amarok/ui/AppListAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,18 @@

import com.google.android.material.checkbox.MaterialCheckBox;

import java.util.List;
import java.util.Objects;
import java.util.Set;

import deltazero.amarok.PrefMgr;
import deltazero.amarok.R;
import deltazero.amarok.utils.AppInfoUtil;

public class AppListAdapter extends ListAdapter<AppInfo, AppListAdapter.AppListHolder> {

private final PackageManager pkgMgr;
private final PrefMgr prefMgr;
private final AppInfoUtil appInfoUtil;

private final LayoutInflater inflater;
private Set<String> hiddenApps;

private static final HandlerThread backgroundThread = new HandlerThread("APP_ADAPTER_THREAD");
private final Handler backgroundHandler;
Expand Down Expand Up @@ -63,14 +59,13 @@ public boolean areContentsTheSame(@NonNull AppInfo oldItem, @NonNull AppInfo new
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());

pkgMgr = activity.getPackageManager();
prefMgr = new PrefMgr(activity);
appInfoUtil = new AppInfoUtil(activity);

this.activity = activity;
this.srLayout = srLayout;

update(null, true);
update(null, true, false);
}

@NonNull
Expand All @@ -92,7 +87,7 @@ public void onBindViewHolder(@NonNull AppListAdapter.AppListHolder holder, int p
}


public void update(String query, boolean fullUpdate) {
public void update(String query, boolean fullUpdate, boolean includeSystemApps) {

// Refreshing thread lock
if (isRefreshing) return;
Expand All @@ -102,21 +97,10 @@ public void update(String query, boolean fullUpdate) {

backgroundHandler.post(() -> {
// Refresh installed apps
if (fullUpdate) appInfoUtil.update();
// Get app info
List<AppInfo> lsAppInfo = appInfoUtil.getInstalledApps(query);
// Sort with app name
hiddenApps = prefMgr.getHideApps();
lsAppInfo.sort((o1, o2) -> {
if (hiddenApps.contains(o1.packageName) && !hiddenApps.contains(o2.packageName))
return -1;
if (hiddenApps.contains(o2.packageName) && !hiddenApps.contains(o1.packageName))
return 1;
return (o1.label.compareTo(o2.label));
});
if (fullUpdate) appInfoUtil.refresh();
// Notify update
activity.runOnUiThread(() -> {
submitList(lsAppInfo);
submitList(appInfoUtil.getFilteredApps(query, includeSystemApps));
srLayout.setRefreshing(false);
isRefreshing = false;
});
Expand Down Expand Up @@ -146,7 +130,7 @@ public AppListHolder(View view) {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String appPkgName = getCurrentList().get(getLayoutPosition()).packageName;
hiddenApps = prefMgr.getHideApps();
var hiddenApps = prefMgr.getHideApps();

if (buttonView.isChecked()) {
hiddenApps.add(appPkgName);
Expand Down
70 changes: 46 additions & 24 deletions app/src/main/java/deltazero/amarok/ui/SetHideAppActivity.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package deltazero.amarok.ui;

import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.core.view.MenuProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
Expand All @@ -20,6 +25,8 @@ public class SetHideAppActivity extends AppCompatActivity {
private MaterialToolbar tbToolBar;
private SwipeRefreshLayout srLayout;

private boolean showSystemApps = false;

private String query = null;

@Override
Expand All @@ -41,36 +48,51 @@ protected void onCreate(Bundle savedInstanceState) {
tbToolBar.setNavigationOnClickListener(v -> finish());

// Setup onRefresh listener
srLayout.setOnRefreshListener(() -> adapter.update(query, true));
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_hideapp, menu);

SearchView searchView = (SearchView) menu.findItem(R.id.action_search_app).getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean once = false;
srLayout.setOnRefreshListener(() -> adapter.update(query, true, showSystemApps));

// Setup menu
addMenuProvider(new MenuProvider() {
@Override
public boolean onQueryTextSubmit(String query) {
return true;
public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
menuInflater.inflate(R.menu.menu_hideapp, menu);

menu.findItem(R.id.display_system_apps).setChecked(showSystemApps);

SearchView searchView = (SearchView) menu.findItem(R.id.action_search_app).getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean once = false;

@Override
public boolean onQueryTextSubmit(String query) {
return true;
}

@Override
public boolean onQueryTextChange(String newText) {
query = newText.isEmpty() ? null : newText;
if (once) adapter.update(newText, false, showSystemApps);
else once = true;
return true;
}
});
searchView.setOnCloseListener(() -> {
query = null;
adapter.update(null, false, showSystemApps);
return true;
});
}

@Override
public boolean onQueryTextChange(String newText) {
query = newText.isEmpty() ? null : newText;
if (once) adapter.update(newText, false);
else once = true;
return true;
public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
if (menuItem.getItemId() == R.id.display_system_apps) {
showSystemApps = !showSystemApps;
menuItem.setChecked(showSystemApps);
adapter.update(query, false, showSystemApps);
return true;
}
return false;
}
});
searchView.setOnCloseListener(() -> {
query = null;
adapter.update(null, false);
return true;
});

return super.onCreateOptionsMenu(menu);
}

}
50 changes: 37 additions & 13 deletions app/src/main/java/deltazero/amarok/utils/AppInfoUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import deltazero.amarok.PrefMgr;

public class AppInfoUtil {
private final PackageManager pkgMgr;
private List<AppInfo> appInfoList = new ArrayList<>();
private final PrefMgr prefMgr;
private final List<AppInfo> appInfoList = new ArrayList<>();

public AppInfoUtil(Context context) {
pkgMgr = context.getPackageManager();
prefMgr = new PrefMgr(context);
}

private static boolean containsIgnoreCase(String str, String searchStr) {
Expand All @@ -36,37 +42,53 @@ private static boolean containsIgnoreCase(String str, String searchStr) {
return false;
}

public void update() {
public void refresh() {
appInfoList.clear();

Set<String> hiddenApps = prefMgr.getHideApps();

// Get applications info
List<ApplicationInfo> installedApplications = pkgMgr.getInstalledApplications(GET_META_DATA | MATCH_DISABLED_COMPONENTS | MATCH_UNINSTALLED_PACKAGES);
for (ApplicationInfo applicationInfo : installedApplications) {

// Ignore system application
if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM)
continue;

// Ignore Amarok itself
// Filter out Amarok itself
if (applicationInfo.packageName.contains("deltazero.amarok"))
continue;

var appInfo = new AppInfo(
applicationInfo.packageName,
pkgMgr.getApplicationLabel(applicationInfo).toString(),
(applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM,
pkgMgr.getApplicationIcon(applicationInfo)
);

appInfoList.add(appInfo);
}

// Sort with app name, and stick the hidden apps to the top
appInfoList.sort((o1, o2) -> {
if (hiddenApps.contains(o1.packageName) && !hiddenApps.contains(o2.packageName))
return -1;
if (hiddenApps.contains(o2.packageName) && !hiddenApps.contains(o1.packageName))
return 1;
return (o1.label.compareTo(o2.label));
});
}

public List<AppInfo> getInstalledApps(String query) {
public List<AppInfo> getFilteredApps(String query, boolean includeSystemApps) {
Set<String> hiddenApps = prefMgr.getHideApps();
Log.d("AppInfoUtil", "Hidden apps: " + hiddenApps.toString());
List<AppInfo> queryAppInfoList = new ArrayList<>();
for (AppInfo appInfo: appInfoList) {
// Apply query filter
if (query == null || query.isEmpty() ||
containsIgnoreCase(appInfo.packageName, query) || containsIgnoreCase(appInfo.label, query))
for (AppInfo appInfo : appInfoList) {

boolean query_filter_result = (query == null || query.isEmpty())
|| containsIgnoreCase(appInfo.packageName, query) || containsIgnoreCase(appInfo.label, query);

boolean system_filter_result = hiddenApps.contains(appInfo.packageName) /* If the app is hidden, show it regardless of whether it is a system app or not. */
|| includeSystemApps /* Skip this filter if user enable `Display system apps` */
|| !appInfo.isSystemApp;

if (query_filter_result && system_filter_result)
queryAppInfoList.add(appInfo);
}
return queryAppInfoList;
Expand All @@ -77,11 +99,13 @@ public static class AppInfo {
public String packageName;
@NonNull
public String label;
public boolean isSystemApp;
public Drawable icon;

public AppInfo(@NonNull String packageName, @NonNull String label, Drawable icon) {
public AppInfo(@NonNull String packageName, @NonNull String label, boolean isSystemApp, Drawable icon) {
this.packageName = packageName;
this.label = label;
this.isSystemApp = isSystemApp;
this.icon = icon;
}
}
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_filter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:alpha="0.9" android:height="20dp"
android:viewportHeight="960" android:viewportWidth="960"
android:width="20dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M440,800q-17,0 -28.5,-11.5T400,760v-240L161,215q-14,-17 -4,-36t31,-19h584q21,0 31,19t-4,36L560,520v240q0,17 -11.5,28.5T520,800h-80ZM480,524 L720,220L240,220l240,304ZM480,524Z"/>
</vector>
8 changes: 4 additions & 4 deletions app/src/main/res/drawable/ic_search.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M15.938,17 L10.958,12.021Q10.333,12.479 9.583,12.74Q8.833,13 8,13Q5.917,13 4.458,11.542Q3,10.083 3,8Q3,5.917 4.458,4.458Q5.917,3 8,3Q10.083,3 11.542,4.458Q13,5.917 13,8Q13,8.833 12.74,9.583Q12.479,10.333 12.021,10.958L17,15.938ZM8,11.5Q9.458,11.5 10.479,10.479Q11.5,9.458 11.5,8Q11.5,6.542 10.479,5.521Q9.458,4.5 8,4.5Q6.542,4.5 5.521,5.521Q4.5,6.542 4.5,8Q4.5,9.458 5.521,10.479Q6.542,11.5 8,11.5Z"/>
</vector>
android:pathData="M796,839L533,576Q503,602 463.04,616.5Q423.08,631 378,631Q269.84,631 194.92,556Q120,481 120,375Q120,269 195,194Q270,119 376.5,119Q483,119 557.5,194Q632,269 632,375.15Q632,418 618,458Q604,498 576,533L840,795L796,839ZM377,571Q458.25,571 515.13,513.5Q572,456 572,375Q572,294 515.13,236.5Q458.25,179 377,179Q294.92,179 237.46,236.5Q180,294 180,375Q180,456 237.46,513.5Q294.92,571 377,571Z"/>
</vector>
18 changes: 16 additions & 2 deletions app/src/main/res/menu/menu_hideapp.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,21 @@
android:id="@+id/action_search_app"
android:icon="@drawable/ic_search"
android:title="@string/search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView" />
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always|collapseActionView" />

<item
android:id="@+id/action_filter_app"
android:icon="@drawable/ic_filter"
android:title="@string/filter"
app:showAsAction="ifRoom">
<menu>
<group android:checkableBehavior="all">
<item
android:id="@+id/display_system_apps"
android:title="@string/display_system_apps" />
</group>
</menu>
</item>

</menu>
2 changes: 2 additions & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,6 @@
<string name="force_unhide_description">尝试恢复所有隐藏的文件与应用。仅在出现问题时使用。</string>
<string name="force_unhide_confirm_msg">尝试恢复所有隐藏的文件与应用?\n\n<b>警告:</b>此功能可能导致文件损坏。请仅当无法正常取消隐藏时使用。</string>
<string name="performing_force_unhide">正在尝试取消隐藏。请稍后。</string>
<string name="filter">筛选</string>
<string name="display_system_apps">显示系统应用</string>
</resources>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,6 @@
<string name="force_unhide_description">Try to restore everything. Use this option only when you run into troubles.</string>
<string name="force_unhide_confirm_msg">Try to restore everything?\n\n<b>WARNING: </b>This option may corrupt your files. Only use it when you encounter trouble and cannot perform the \"unhide\" function normally.</string>
<string name="performing_force_unhide">Executing force unhide. Please sit tight.</string>
<string name="filter">Filter</string>
<string name="display_system_apps">Display system apps</string>
</resources>

0 comments on commit 1c314b6

Please sign in to comment.