From e8fc91191f7e41b251a8c54244ffc2131487e964 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 1 Aug 2020 18:20:39 -0700 Subject: [PATCH] Add the option to hide games in the app list Fixes #640 --- app/src/main/java/com/limelight/AppView.java | 56 +++++++++++++++- app/src/main/java/com/limelight/PcView.java | 22 +++++-- .../com/limelight/grid/AppGridAdapter.java | 64 ++++++++++++++++--- app/src/main/res/values/strings.xml | 4 +- 4 files changed, 128 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/limelight/AppView.java b/app/src/main/java/com/limelight/AppView.java index d9210672..1ce7a746 100644 --- a/app/src/main/java/com/limelight/AppView.java +++ b/app/src/main/java/com/limelight/AppView.java @@ -2,6 +2,7 @@ package com.limelight; import java.io.IOException; import java.io.StringReader; +import java.util.HashSet; import java.util.List; import com.limelight.computers.ComputerManagerListener; @@ -26,6 +27,7 @@ import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; @@ -59,6 +61,8 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { private int lastRunningAppId; private boolean suspendGridUpdates; private boolean inForeground; + private boolean showHiddenApps; + private HashSet hiddenAppIds = new HashSet<>(); private final static int START_OR_RESUME_ID = 1; private final static int QUIT_ID = 2; @@ -66,10 +70,14 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { private final static int START_WITH_QUIT = 4; private final static int VIEW_DETAILS_ID = 5; private final static int CREATE_SHORTCUT_ID = 6; + private final static int HIDE_APP_ID = 7; + + public final static String HIDDEN_APPS_PREF_FILENAME = "HiddenApps"; public final static String NAME_EXTRA = "Name"; public final static String UUID_EXTRA = "UUID"; public final static String NEW_PAIR_EXTRA = "NewPair"; + public final static String SHOW_HIDDEN_APPS_EXTRA = "ShowHiddenApps"; private ComputerManagerService.ComputerManagerBinder managerBinder; private final ServiceConnection serviceConnection = new ServiceConnection() { @@ -98,13 +106,16 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { try { appGridAdapter = new AppGridAdapter(AppView.this, PreferenceConfiguration.readPreferences(AppView.this), - computer, localBinder.getUniqueId()); + computer, localBinder.getUniqueId(), + showHiddenApps); } catch (Exception e) { e.printStackTrace(); finish(); return; } + appGridAdapter.updateHiddenApps(hiddenAppIds); + // Now make the binder visible. We must do this after appGridAdapter // is set to prevent us from reaching updateUiWithServerinfo() and // touching the appGridAdapter prior to initialization. @@ -285,8 +296,14 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { UiHelper.notifyNewRootView(this); + showHiddenApps = getIntent().getBooleanExtra(SHOW_HIDDEN_APPS_EXTRA, false); uuidString = getIntent().getStringExtra(UUID_EXTRA); + SharedPreferences hiddenAppsPrefs = getSharedPreferences(HIDDEN_APPS_PREF_FILENAME, MODE_PRIVATE); + for (String hiddenAppIdStr : hiddenAppsPrefs.getStringSet(uuidString, new HashSet())) { + hiddenAppIds.add(Integer.parseInt(hiddenAppIdStr)); + } + String computerName = getIntent().getStringExtra(NAME_EXTRA); TextView label = findViewById(R.id.appListText); @@ -298,6 +315,21 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { Service.BIND_AUTO_CREATE); } + private void updateHiddenApps() { + HashSet hiddenAppIdStringSet = new HashSet<>(); + + for (Integer hiddenAppId : hiddenAppIds) { + hiddenAppIdStringSet.add(hiddenAppId.toString()); + } + + getSharedPreferences(HIDDEN_APPS_PREF_FILENAME, MODE_PRIVATE) + .edit() + .putStringSet(uuidString, hiddenAppIdStringSet) + .apply(); + + appGridAdapter.updateHiddenApps(hiddenAppIds); + } + private void populateAppGridWithCache() { try { // Try to load from cache @@ -368,7 +400,12 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { menu.add(Menu.NONE, CANCEL_ID, 2, getResources().getString(R.string.applist_menu_cancel)); } } - menu.add(Menu.NONE, VIEW_DETAILS_ID, 3, getResources().getString(R.string.applist_menu_details)); + + MenuItem hideAppItem = menu.add(Menu.NONE, HIDE_APP_ID, 3, getResources().getString(R.string.applist_menu_hide_app)); + hideAppItem.setCheckable(true); + hideAppItem.setChecked(selectedApp.isHidden); + + menu.add(Menu.NONE, VIEW_DETAILS_ID, 4, getResources().getString(R.string.applist_menu_details)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Only add an option to create shortcut if box art is loaded @@ -379,7 +416,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { BitmapDrawable drawable = (BitmapDrawable)appImageView.getDrawable(); if (drawable != null && drawable.getBitmap() != null) { // We have a bitmap loaded too - menu.add(Menu.NONE, CREATE_SHORTCUT_ID, 4, getResources().getString(R.string.applist_menu_scut)); + menu.add(Menu.NONE, CREATE_SHORTCUT_ID, 5, getResources().getString(R.string.applist_menu_scut)); } } } @@ -437,6 +474,18 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { Dialog.displayDialog(AppView.this, getResources().getString(R.string.title_details), app.app.toString(), false); return true; + case HIDE_APP_ID: + if (item.isChecked()) { + // Transitioning hidden to shown + hiddenAppIds.remove(app.app.getAppId()); + } + else { + // Transitioning shown to hidden + hiddenAppIds.add(app.app.getAppId()); + } + updateHiddenApps(); + return true; + case CREATE_SHORTCUT_ID: ImageView appImageView = info.targetView.findViewById(R.id.grid_image); Bitmap appBits = ((BitmapDrawable)appImageView.getDrawable()).getBitmap(); @@ -593,6 +642,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { public static class AppObject { public final NvApp app; public boolean isRunning; + public boolean isHidden; public AppObject(NvApp app) { if (app == null) { diff --git a/app/src/main/java/com/limelight/PcView.java b/app/src/main/java/com/limelight/PcView.java index a8e2aff6..61d86247 100644 --- a/app/src/main/java/com/limelight/PcView.java +++ b/app/src/main/java/com/limelight/PcView.java @@ -117,6 +117,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { private final static int RESUME_ID = 6; private final static int QUIT_ID = 7; private final static int VIEW_DETAILS_ID = 8; + private final static int FULL_APP_LIST_ID = 9; private void initializeViews() { setContentView(R.layout.activity_pc_view); @@ -333,12 +334,13 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { } menu.add(Menu.NONE, APP_LIST_ID, 3, getResources().getString(R.string.pcview_menu_app_list)); + menu.add(Menu.NONE, FULL_APP_LIST_ID, 4, getResources().getString(R.string.pcview_menu_full_app_list)); // FIXME: We used to be able to unpair here but it's been broken since GFE 2.1.x, so I've replaced // it with delete which actually work - menu.add(Menu.NONE, DELETE_ID, 4, getResources().getString(R.string.pcview_menu_delete_pc)); + menu.add(Menu.NONE, DELETE_ID, 5, getResources().getString(R.string.pcview_menu_delete_pc)); } - menu.add(Menu.NONE, VIEW_DETAILS_ID, 5, getResources().getString(R.string.pcview_menu_details)); + menu.add(Menu.NONE, VIEW_DETAILS_ID, 6, getResources().getString(R.string.pcview_menu_details)); } @Override @@ -442,7 +444,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { if (toastSuccess) { // Open the app list after a successful pairing attempt - doAppList(computer, true); + doAppList(computer, true, false); } else { // Start polling again if we're still in the foreground @@ -541,7 +543,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { }).start(); } - private void doAppList(ComputerDetails computer, boolean newlyPaired) { + private void doAppList(ComputerDetails computer, boolean newlyPaired, boolean showHiddenGames) { if (computer.state == ComputerDetails.State.OFFLINE) { Toast.makeText(PcView.this, getResources().getString(R.string.error_pc_offline), Toast.LENGTH_SHORT).show(); return; @@ -555,6 +557,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { i.putExtra(AppView.NAME_EXTRA, computer.name); i.putExtra(AppView.UUID_EXTRA, computer.uuid); i.putExtra(AppView.NEW_PAIR_EXTRA, newlyPaired); + i.putExtra(AppView.SHOW_HIDDEN_APPS_EXTRA, showHiddenGames); startActivity(i); } @@ -592,8 +595,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { }, null); return true; + case FULL_APP_LIST_ID: case APP_LIST_ID: - doAppList(computer.details, false); + doAppList(computer.details, false, item.getItemId() == FULL_APP_LIST_ID); return true; case RESUME_ID: @@ -635,6 +639,12 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { new DiskAssetLoader(this).deleteAssetsForComputer(details.uuid); + // Delete hidden games preference value + getSharedPreferences(AppView.HIDDEN_APPS_PREF_FILENAME, MODE_PRIVATE) + .edit() + .remove(details.uuid) + .apply(); + for (int i = 0; i < pcGridAdapter.getCount(); i++) { ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(i); @@ -711,7 +721,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { // Pair an unpaired machine by default doPair(computer.details); } else { - doAppList(computer.details, false); + doAppList(computer.details, false, false); } } }); diff --git a/app/src/main/java/com/limelight/grid/AppGridAdapter.java b/app/src/main/java/com/limelight/grid/AppGridAdapter.java index 10857fb4..a7a65c79 100644 --- a/app/src/main/java/com/limelight/grid/AppGridAdapter.java +++ b/app/src/main/java/com/limelight/grid/AppGridAdapter.java @@ -17,8 +17,12 @@ import com.limelight.grid.assets.NetworkAssetLoader; import com.limelight.nvstream.http.ComputerDetails; import com.limelight.preferences.PreferenceConfiguration; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; @SuppressWarnings("unchecked") public class AppGridAdapter extends GenericGridAdapter { @@ -28,18 +32,38 @@ public class AppGridAdapter extends GenericGridAdapter { private final ComputerDetails computer; private final String uniqueId; + private final boolean showHiddenApps; private CachedAppAssetLoader loader; + private Set hiddenAppIds = new HashSet<>(); + private ArrayList allApps = new ArrayList<>(); - public AppGridAdapter(Context context, PreferenceConfiguration prefs, ComputerDetails computer, String uniqueId) { + public AppGridAdapter(Context context, PreferenceConfiguration prefs, ComputerDetails computer, String uniqueId, boolean showHiddenApps) { super(context, getLayoutIdForPreferences(prefs)); this.computer = computer; this.uniqueId = uniqueId; + this.showHiddenApps = showHiddenApps; updateLayoutWithPreferences(context, prefs); } + public void updateHiddenApps(Set newHiddenAppIds) { + this.hiddenAppIds.clear(); + this.hiddenAppIds.addAll(newHiddenAppIds); + + // Reconstruct the itemList with the new hidden app set + itemList.clear(); + for (AppView.AppObject app : allApps) { + app.isHidden = hiddenAppIds.contains(app.app.getAppId()); + + if (!app.isHidden || showHiddenApps) { + itemList.add(app); + } + } + notifyDataSetChanged(); + } + private static int getLayoutIdForPreferences(PreferenceConfiguration prefs) { if (prefs.smallIconMode) { return R.layout.app_grid_item_small; @@ -88,8 +112,8 @@ public class AppGridAdapter extends GenericGridAdapter { loader.freeCacheMemory(); } - private void sortList() { - Collections.sort(itemList, new Comparator() { + private static void sortList(List list) { + Collections.sort(list, new Comparator() { @Override public int compare(AppView.AppObject lhs, AppView.AppObject rhs) { return lhs.app.getAppName().toLowerCase().compareTo(rhs.app.getAppName().toLowerCase()); @@ -98,16 +122,33 @@ public class AppGridAdapter extends GenericGridAdapter { } public void addApp(AppView.AppObject app) { - // Queue a request to fetch this bitmap into cache - loader.queueCacheLoad(app.app); + // Update hidden state + app.isHidden = hiddenAppIds.contains(app.app.getAppId()); - // Add the app to our sorted list - itemList.add(app); - sortList(); + // Always add the app to the all apps list + allApps.add(app); + sortList(allApps); + + // Add the app to the adapter data if it's not hidden + if (showHiddenApps || !app.isHidden) { + // Queue a request to fetch this bitmap into cache + loader.queueCacheLoad(app.app); + + // Add the app to our sorted list + itemList.add(app); + sortList(itemList); + } } public void removeApp(AppView.AppObject app) { itemList.remove(app); + allApps.remove(app); + } + + @Override + public void clear() { + super.clear(); + allApps.clear(); } @Override @@ -123,5 +164,12 @@ public class AppGridAdapter extends GenericGridAdapter { else { overlayView.setVisibility(View.GONE); } + + if (obj.isHidden) { + parentView.setAlpha(0.40f); + } + else { + parentView.setAlpha(1.0f); + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34768c11..c57df24e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,7 +15,8 @@ Loading help page… - View Game List + View Apps + View Hidden Apps Pair with PC Unpair Send Wake-On-LAN request @@ -99,6 +100,7 @@ View Details Create Shortcut Add to Channel + Hide App App List Refreshing apps… Error